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.
Related
I'm trying to get a more concrete understanding of MVC and keeping the controller layer as thin as possible.
One thing I keep asking myself is "Where should I call modelname->save()?"
Looking at the Laravel documentation, they set data to the model and call save in the controller which doesn't seem right...
<?php
namespace App\Http\Controllers;
use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class FlightController extends Controller
{
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
This is quite a simple example and might be why they do it all in the controller.
From my understanding and everything I've been reading, all business logic should sit inside the model, the controller is responsible for "traffic control" between the view and the model.
So would I be calling save inside the model itself? or should I be using a service layer?
Here is my current problem with example data.
I am updating the status of a model. The row already exists in the DB. I use PATCH /route/ to get to the controller method. From there I get the model.
class TimecardController extends Controller {
...
public function markAsPass(Request $request, $id) {
$test = Test::findOrFail($id);
//I don't think this is the corect way
//$test->status = "passed";
//$test->markedBy = "Teacher123";
//$test->save();
$test->passed();
...
return redirect($redirect_url);
}
}
class Test extends Model {
...
public function passed() {
$this->status = "passed";
//would I call save here?
//$this->save();
}
}
Do I take an approach like above? Or do I create a service layer where I would use the model instance to call the model functions and then call save on the model?
//in service class
public function makeTestAsPassed($test){
$test->passed();
$test->save();
}
Please let me know if any claification is needed.
You’re right in that business logic belongs in models. If you take a “resourceful” approach to your applications (in that you create controllers around entities) then you’ll find that your controller actions seldom call more than one model method.
Instead of calling save(), you can call create() and update() methods on your model. In your store() controller action, you can create a new entity with one line like this:
public function store(CreateRequest $request)
{
$model = Model::create($request->all());
}
And update an existing model in an update() action like this:
public function update(UpdateRequest $request, Model $model)
{
$model->update($request->all());
}
When it comes to business logic, you can call other methods on your models, too. To use resourceful controllers, you don’t have to have a model that relates to a database table.
Take shipping an order. Most people would be tempted to put a ship() method in an OrderController, but what happens when you ship an order? What entity could shipping an order result in? Well, you’d be creating a shipment, so that could instead be a store() method on an OrderShipmentController. This store() method could then just call a ship() method on your Order model:
class OrderShipmentController extends Controller
{
public function store(ShipOrderRequest $request, Order $order)
{
$order->ship();
}
}
So as you can see, with resourceful controllers and route–model binding, you can have “skinny controllers” with your application’s business logic living in your models.
MVC is designed for ease of maintenance.
The approach you don't recognize as "good" is the proper approach. All data handling related to business logic goes in the controller. Otherwise, a different coder will be confused as he/she would not find the data manipulation logic in the controller code.
Your thin controller goal defeats MVC.
Also note the model code is purposed to be thin as it is a place to define the database schema as mirror image to the database tables.
MVC is not object oriented abstration. MVC is a structure for code maintenance uniformity.
I keen to make my code decouple and ready for testing.
I have an Eloquent model getBudgetConvertedAttribute is depend on sentry user attribute.
public function getBudgetConvertedAttribute()
{
return Sentry::getUser()->currency * $this->budget;
}
This throw error while testing because Sentry::getUser is return null.
My question is, How shall I code to inject user into model from controller or service provider binding or testing?
Inject a $sentry object as a dependency in the constructor instead of using the Sentry Facade.
Example
use Path\To\Sentry;
class ClassName
{
protected $sentry
public function __construct(Sentry $sentry)
{
$this->sentry = $sentry;
}
public function methodName()
{
$this->sentry->sentryMethod();
}
}
Why not just create a method on the model, then takes a Sentry user object as a parameter?
public function getBudgetConverted(SentryUser $user)
{
return $user->currency * $this->budget;
}
You’ll need to change the type-hint (SentryUser) to the actual name of your user class.
If this is to aid testing, you could go one step furhter and type-hint on an interface (which you should be any way), that way you could test your method with a mock user object rather than one that may have a load of other dependencies like a database connection, which Eloquent models do.
Take a look to that code
<?php
namespace Sestante\SestanteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Sestante\SestanteBundle\Model\StrutturaManager;
class MainController extends Controller
{
public function indexAction(Request $request)
{
return $this->render('SestanteSestanteBundle:Main:index.html.twig');
}
public function showLodgingsAction(Request $request)
{
$repo = $this->getDoctrine()->getRepository('SestanteSestanteBundle:Struttura');
$usr = $this->get('security.context')->getToken()->getUser();
$usrId = $usr->getId();
$sm = new StrutturaManager($repo);
$lodgingList = $sm->retrieveLodgingsFromUser($usrId);
return $this->render('SestanteSestanteBundle:Main:showLodgings.html.twig',array('lodgingList' => $lodgingList));
}
}
This is a controller for an application that I've been writing.
Take a look to showLodgingsAction. I've tryied to place all business logic into a model (StrutturaManager) that with a repository (that I've passed directly from controller because, as far I know, they're available only here or via D.I.) query my db, do some elaboration and return a list that I'll render onto a template.
First question: Is this "code separation" good, or exists a better way for do what I'm trying to do?
Second question: suppose that, now, I want to use an object of StrutturaManager type into indexAction. Remember that mine object want a repository. So, have I to declare again, and again, and again, all my object for every controller's action where I want to use them? I suppose that must exist a smarter method but, at the moment, I don't understand which.
Define StrutturaManager as a service and inject the EntityManager into it. This way the manager will have access to repositories you need and controllers won't know about Doctrine nor repositories — which is a good practice.
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
I´m having problems understanding some basic strategies using doctrine 2 in codeIgniter 2 development.
Background: CI is up and running with doctrine, I can get entities from database and save them.
Example:
I have few controllers, where I would like to list latest articles. In my pure CI application I would have 'getLatest' -method in my model. I then would call this in all of my controllers, loading correct view etc.
But now I have doctrine models and not sure how to do this. I just cant add same method to my model. What I have done is moved that getLatest-logic to controller and this does not look right. Now I would need to call other controllers from my actions to get these latest articles. Or should I really duplicate that code in every controller where I need it?
I am still struggling with this. My CI models and doctrine entities have same names and are located in same "application/models/" folder. These together cause several problems. I am trying to change this path, but cant get it work. I have used this library class for loading doctrine: http://wildlyinaccurate.com/integrating-doctrine-2-with-codeigniter-2/ Any tips?
Your getLatest functionality belongs in the model, not the controller. Each of your Doctrine 2 models has its own Repository, which you can get with $em->getRepository('Your\Model'). The default repository (\Doctrine\ORM\EntityRepository) has several methods that might be useful to you:
$repository = $em->getRepository('Your\Model');
$all_entities = $repository->findAll();
$some_entities = $repository->findBy(array(
'some_column' => 'some_condition'
), $order_by, $limit, $offset);
If Doctrine's built-in repository doesn't do what you need, you can create your own repository for any of your models:
class YourRepository extends EntityRepository
{
public function getLatest()
{
// Your getLatest logic here
return $this->_em->createQuery('SELECT m FROM Your\Model LIMIT 10');
}
}
Instead of having a "Model", you can wrap Doctrine components in a Service and keep your controller as simple as possible. Using MVC framework or not, it is important to follow SOLID, especially the Single Responsibility Principle (SRP).
Consider the following simplified example:
class UserService extends AbstractService
{
private $em;
private $repository;
public function __construct(EntityManager $em, UserRepository $repository)
{
$this->em = $em;
$this->repository = $repository;
}
public function create($data)
{
// business logic here
$user = new User();
$user->setUsername($data['username']);
$user->setEmail($data['email']);
$em->persist($this->user);
$em->flush();
}
public function getLatest()
{
// additional business logic could be implemented
return $this->repository->getLatest();
}
}
Then I can call the service from the Controller or from any other service:
class UserController extends Controller
{
/**
* #var UserService $service
*/
private $service;
public function __construct(UserService $service)
{
$this->service = $service;
}
public function create()
{
// some form validation here
$data = $this->input->post('user');
$result = $this->service->create($data);
// do something with the result
}
}
No, you should not neither duplicate nor move that code to controllers. Haven't tried that personally, but I think that doctrine Entities and CI models are completely independent. You may introduce some CI's models that incorporate that getLatest method, but really operate on Doctrine entities.
So you'll just use Doctrine entities and CI's models as usual. But don't forget to load doctrine library (if you bootstrapped as told in Doctrine manual) into models that use Doctrine entities.