I've got 2 classes in my application both have their own Controller.
Week -> WeekController
Day -> DayController
When a new week is created I also want to create 5 (work)days for that week.
At this point I make a call from the create method in the WeekController to the create method in the DayController:
app('App\Http\Controllers\DayContoller')->create($week->id);
This post mentions this as being a bad practice. The alternatives offered however don't seem better in my case:
A week doesn't really extend a day and using a trait would feel strange to me in this scenario as well.
So I wonder, what's the best solution in this scenario. I'm pretty sure this is something that's pretty common.
UPDATE: Not quite sure if this is better but I already rewrote as follows:
I'm now injecting the DayController in the create method of the WeekController.
create(DayController $dayController, $id)
UPDATE2: I wrote a closure function in my route where I call a new written service.
function(CreateWeek $createWeek, $id)
{
$createWeek->createWeekWithDays($id);
} ]);
In this service I have a constructor that instantiates my 2 Controllers
public function __construct(WeekController $weekController, DayController $dayController)
{
$this->weekController = $weekController;
$this->dayController = $dayController;
}
and finally the createWeekWithDays method:
public function createWeekWithDays($id)
{
//Create the week
$newId = $this->weekController->create($id);
// Create the days
$this->dayController->createDays($newId);
}
I don't know what's the best solution...
Related
I know DI, Solid Principles, factory patterns, adapter pattern and many more maybe. Now let's say I am creating a laravel application and it's gonna be huge. Let's say I have a postscontroller which is resource and has CRUD methods. Now Let's say in that controller's functions, I have a post Model and use it to retrieve data from database. I have a store function where I am creating new Post() and then put it into database.
1) Is it a good practice to have Post model directly in PostController's function and use new Post() also? What is bad in it? I know that this way I am not using dependency injection and patterns, but still why is it bad? As you know , I can still mock the object without dependency injection since laravel has so many amazing testing features. Then why is it that bad to write new keyword in controller's functions and also use Post model directly?
To give you one answer to your question. There is this "Slim controllers, fat models" concept. I used to defer the object creation to the object itself by Named Constructers.
class UserController
{
public function create(UserCreateRequest $request)
{
$user = User::createFromRequest($request);
// do anything else
}
}
class User
{
public static function createFromRequest(UserCreateRequest $request)
{
$user = new User;
$user->first_name = $request->first_name;
// ...
$user->save();
return $user;
}
}
With this you can have more different constructors like User::createAdmin and its testable. You just need to mock the Request.
Im really curious how does codeigniter achieve something like this:
$this->upload->do_upload($field_name)
it looks like method chaining, but it's not. How would the structure of this look in plain OOP?
I suppose its not as simple as..?
public function upload()
{
// stuff
return $this;
}
public function do_upload()
{
// stuff
return $foo;
}
Cheers!
When you load the library in your controller it's actually doing something like this behind the scene.
include 'system/libraries/Upload.php';
$this->upload = new CI_Upload();
Now you have "$this->upload" ready to use,
Next when you call "$this->upload->do_upload()" you're actually calling a method within the library.
On the other hand Method chaining, is just a matter of making methods returns an instance of the same object, You can review this in libraries code in CodeIgniter 3 on GitHub.
Where most library uses method chaining now.
As I work with third-party APIs pretty frequently, I thought it would be helpful to create some Magento modules to enable easy connection and querying of them. Ideally, you could query an API like this...
$data = Mage::getModel( 'tools/apixyz_list' )->getCollection();
It would instantiate a model for one of the list items, then attempt to get a collection of them by querying the API. This would require some kind of hookup in the config between the Resource Model and the API and that's where I'm having a little trouble.
Is there a recommended way to do this? I'm having a lot of difficulty finding anything on the subject yet I feel like it should be a pretty common issue given the amount of APIs that generally need to get integrated from project to project.
Yes! I actually built this for Recurly - I'm trying to get it open sourced, but it's not open yet. Here's a snippet from the load() method which is the guts of it.
// TBT_Recurly_Model_Resource_Recurly_Abstract_Collection
public function load($printQuery = false, $logQuery = false)
{
if ($this->isLoaded()) {
return $this;
}
if ($this->_isCached()) {
return $this->_loadCache();
}
$this->_beforeLoad();
$this->_renderFilters()
->_renderOrders()
->_renderLimit();
$this->clear();
try {
# This is ultimately doing the API call
$recurly_list = $this->_getListSafe();
} catch (Recurly_Error $e) {
Mage::logException($e);
$this->setConnectionError($e->getMessage());
return $this;
}
foreach ($recurly_list as $recurly_item)
{
$item = $this->getNewEmptyItem();
$item->getResource()->setDataOnObject($item, $recurly_item);
// Recurly appears to sometimes return duplicate subscription items in it's list response.
if (!isset($this->_items[$item->getId()])) {
$this->addItem($item);
}
}
$this->_afterLoadRecurly();
// We have to setIsLoaded before we saveCache b/c otherwise it will infinite loop
$this->_setIsLoaded();
$this->_saveCache();
$this->_afterLoad();
return $this;
}
We actually ended up taking this and putting it into a base REST class, and it was really cool because it ended up being really easy to implement new REST APIs on top of it.
As far as best practice goes, I'm not sure that I've answered your question specifically. But basically I think the main things to do to make it clean are:
Follow the Magento models / collection method signatures for querying.
Implement caching
Implement the API communication in the resource model layer
After reading a lot of posts and Stack Overflow resources, I've still got some problems about the famous question about "where to put business logic?" Reading StackOverflow Question and A Blog Post, I believe I've understood the issue of code separation well.
Suppose I have a web form where you can add a user that will be added to a db. This example involves these concepts:
Form
Controller
Entity
Service
Repository
If I didn't miss something, you have to create an entity with some properties, getters, setters and so on in order to make it persist into a db. If you want to fetch or write that entity, you'll use entityManager and, for "non-canonical" query, entityRepository (that is where you can fit your "query language" query).
Now you have to define a service (that is a PHP class with a "lazy" instance) for all business logic; this is the place to put "heavy" code. Once you've recorded the service into your application, you can use it almost everywhere and that involves code reuse and so on.
When you render and post a form, you bind it with your entity (and with constraints of course) and use all the concepts defined above to put all together.
So, "old-me" would write a controller's action in this way:
public function indexAction(Request $request)
{
$modified = False;
if($request->getMethod() == 'POST'){ // submit, so have to modify data
$em = $this->getDoctrine()->getEntityManager();
$parameters = $request->request->get('User'); //form retriving
$id = $parameters['id'];
$user = $em->getRepository('SestanteUserBundle:User')->find($id);
$form = $this->createForm(new UserType(), $user);
$form->bindRequest($request);
$em->flush();
$modified = True;
}
$users = $this->getDoctrine()->getEntityManager()->getRepository('SestanteUserBundle:User')->findAll();
return $this->render('SestanteUserBundle:Default:index.html.twig',array('users'=>$users));
}
"New-me" has refactored code in this way:
public function indexAction(Request $request)
{
$um = $this->get('user_manager');
$modified = False;
if($request->getMethod() == 'POST'){ // submit, so have to modify data
$user = $um->getUserById($request,False);
$form = $this->createForm(new UserType(), $user);
$form->bindRequest($request);
$um->flushAll();
$modified = True;
}
$users = $um->showAllUser();
return $this->render('SestanteUserBundle:Default:index.html.twig',array('users'=>$users));
}
Where $um is a custom service where all code that you can't see from #1 code piece to #2 code piece is stored.
So, here are my questions:
Did I, finally, get the essence of symfony2 and {M}VC in general?
Is the refactor a good one? If not, what would be a better way?
Post Scriptum: I know that I can use the FOSUserBundle for User store and authentication, but this is a basic example for teach myself how to work with Symfony.
Moreover, my service was injected with ORM.Doctrine.* in order to work (just a note for who read this question with my same confusion)
There are two main approaches regarding on where to put the business logic: the SOA architecture and the domain-driven architecture. If your business objects (entities) are anemic, I mean, if they don’t have business logic, just getters and setters, then you will prefer SOA. However, if you build the business logic inside your business objects, then you will prefer the other. Adam Bien discusses these approaches:
Domain-driven design with Java EE 6: http://www.javaworld.com/javaworld/jw-05-2009/jw-05-domain-driven-design.html
Lean service architectures with Java EE 6: http://www.javaworld.com/javaworld/jw-04-2009/jw-04-lean-soa-with-javaee6.html
It’s Java, but you can get the idea.
Is the refactor a good one? If not, what would be a better way?
One of the best framework practices is using param converters to directly invoke an entity from user request.
Example from Symfony documentation:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/blog/{id}")
* #ParamConverter("post", class="SensioBlogBundle:Post")
*/
public function showAction(Post $post)
{
}
More on param converters:
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
Robert C. Martin (the clean code guy) says in his new book clean architecture, that you should put your business logic independently from your framerwork, because the framework will change with time.
So you can put your business logic in a seperate folder like App/Core or App/Manager and avoid inheretence from symfony classes here:
<?php
namespace App\Core;
class UserManager extends BaseManager implements ManagerInterface
{
}
I realize this is an old question, but since I had a similar problem I wanted to share my experience, hoping that it might be of help for somebody.
My suggestion would be to introduce a command bus and start using the command pattern. The workflow is pretty much like this:
Controller receives request and translates it to a command (a form might be used to do that, and you might need some DTO to move data cleanly from one layer to the other)
Controller sends that command to the command bus
The command bus looks up a handler and handles the command
The controller can then generate the response based on what it needs.
Some code based on your example:
public function indexAction(Request $request)
{
$command = new CreateUser();
$form = $this->createForm(new CreateUserFormType(), $command);
$form->submit($request);
if ($form->isValid()) {
$this->get('command_bus')->handle();
}
return $this->render(
'SestanteUserBundle:Default:index.html.twig',
['users' => $this->get('user_manager')->showAllUser()]
);
}
Then your command handler (which is really part of the service layer) would be responsible of creating the user. This has several advantages:
Your controllers are much less likely to become bloated, because they have little to no logic
Your business logic is separated from the application (HTTP) logic
Your code becomes more testable
You can reuse the same command handler but with data coming from a different port (e.g. CLI)
There are also a couple downsides:
the number of classes you need in order to apply this pattern is higher and it usually scales linearly with the number of features your application exposes
there are more moving pieces and it's a bit harder to reason about, so the learning curve for a team might be a little steeper.
A couple command buses worth noting:
https://github.com/thephpleague/tactician
https://github.com/SimpleBus/MessageBus
currently i am doing a project in zend the way i am doing is working perfectly but i am sure its not the way i am suppose to do i mean i am not following MVC and i want to apply MVC in my zend app.
i am pasting code of one simple module which will describe what i am doing .kindly correct me where i am making faults.
my controller
class ContactsController extends Zend_Controller_Action{
public function contactsAction(){
if(!Zend_Auth::getInstance()->hasIdentity()){
$this->_redirect('login/login');
}
else{
$request = $this->getRequest();
$user = new Zend_Session_Namespace('user');
$phone_service_id = $user->p_id;
$instance = new Contacts();
$select = $instance->Get_Contacts($p_id);
$adapter = new Zend_Paginator_Adapter_DbSelect($select);
$paginator = new Zend_Paginator($adapter);
.
.
//more code
}
plz note this 2 line in my controller
$instance = new Contacts();
$select = $instance->Get_Contacts($pid);
this is my contacts class in models
class Contacts extends Zend_Db_Table{
function Get_Contacts($p_id){
$DB = Zend_Db_Table_Abstract::getDefaultAdapter();
$select = $DB->select()
->from('contact', array('contact_id','contact_first_name','contact_mobile_no','contact_home_no','contact_email','contact_office_no'))
->where('pid = ?', $p_id)
->order('date_created DESC');
return $select;
}
}
after this i simple assign my result to my view.
note please
as its working but there is not private data members in my class,my class is not a blue print.there are no SETTERS AND GETTERS .how can i make my code that best suits MVC and OOP??
The most simple answer: you are already almost MVC. You use a Zend_Controller_Action to grab some data and pass this on to a view layer where you render the html. The only missing part is your model, which is mixed up between the controller and your data gateway (where you implemented a table data gateway pattern, that Zend_Db_Table thing).
I gave a pretty thorough explanation in an answer to another question how I'd properly set up the relations between Controller and Model. I also combined this with a Form, to handle data input, filtering and validation. Then to bundle some common functions, I introduced a Service layer between the Model and Controller.
With the controller, you perform some actions (list all my contacts, create a new contact, modify a contact) and the model is purely containing the data (id, name, phone, address). The service helps to group some functions (findContactByName, findContactById, updateContactWithForm).
If you know how to split Controller, Mode, Form and Service, your controller can become something like this:
class ContactsController extends Zend_Controller_Action
{
public function indexAction ()
{
if (!$this->hasIdentity()) {
$this->_redirect('login/login');
}
$service = new Application_Service_Contacts;
$contacts = $service->getContacts();
$paginator = $service->getPaginator($contacts);
$this->view->paginator = $paginator;
}
protected function hasIdentity ()
{
return Zend_Auth::getInstance->hasIdentity();
}
}
It is your personal taste what you want to do in your controller: I'd say you put as less as possible in your controllers, but you need to keep the control. So: a call to get data happens in the controller, retrieving this data happens somewhere else. Also: a call to convert a dataset into something else happens in the controller, the conversion happens somewhere else.
This way you can change the outcome in controllers extremely fast if you provided enough methods to your service classes to fetch the data. (Note I took the Zend_Auth to another function: if you have other actions, you can use this same function. Also, if you want to change something in your authentication, you have one place where this is located instead of every action in the controller)
keep one thing in mind when u learn new technology so first read thier own documentation. No one can explain better than them. Its hard to understand firstly but when you study it you will usedto and than u will love it like me Zend Offical Site