I have a little problem. What data keep in controllers and what in models? I know in models keep whole logic of applications etc, but what's query and helpers functions? for example
Controller:
public function add(Request $request)
{
$item = new Item()
$item->name = $request->name;
$item->save();
$this->makeDirectory();
}
private function makeDirectory()
{
//make a directory with photo this product
}
Where should I keep "makeDirecory" method in controller or models?
This is another situation when i would delete product and reference from another table.
public function delete(Items $id)
{
$id->delete();
$this->deleteProperties($id->properties); // $id->properties is a method from Items model with references to table Properties
}
private function deleteProperties(Properties $id)
{
$id->delete();
}
Should I keep "deleteProperties" method in controller, Items model or Properties model? and invoke this method from this model?
You should keep methods like makeDirectory() in a service class and call it with:
$this->fileService->makeDirectory($directory);
You should keep data related logic in model classes or repository classes and use it in controller with:
$this->model->getSomeData();
You may also want to google "Fat models, skinny controllers".
Regarding helper functions, you should use these only when you really need one. For example, isAdmin() is a very handy global helper, but you should never create helpers like getAllUsers() or Helpers::getAllUsers()
I use controllers only to validate the incoming data and passing data to views.
I add another layer of classes that I call Departments. So, I have a department for profiles, artiles, info pages etc. Each department has its own namespace and a set of classes connected with the functionality.
Always think about SoC - separation of concerns. If you put a lot of logic into a controller, it will eventually get huge, hard to maintain and extend.
Example:
Controller:
public function addItem (Request $request, Item $item, ItemStorage
$itemStorage) {
if ($item->verifyInput($request->all())) {
$itemStorage->createItem ($item, $request->all());
}
else {
// ... handle input error
}
// ... view
}
App\Departments\Items:
class ItemStorage {
public function createItem ($newItem, $attributes) {
$newItem->create($attributes);
// ... prepare data for creating a directory
$this->makeDirectory($directoryName);
}
private function makeDirectory ($directoryName) {
//... create directory
}
}
You can/should separate the tasks even further. ItemStorage might not need to handle actual directory creation. You can call another department/service class name e.g. DiskManagement. This department would contain Classes like FileSystem. So, inside the makeDirectory() method, you would call a method from a class specialized in file system operations.
Related
I have an application where I have to deal with multiple vendors - each having their different implementation. For example, let's say Payment Systems, there are many vendors and banks.
There are a few things common, like implementation process. In each case, I have to give a callback URL to them to give me a response.
For now, I have two vendors - VendorPay and VendorA.
I will have two routes:
payment/callback/vendorpay
payment/callback/vendora
each of them call two methods in controller.
processVendorpay and processVendora
Now, if I want to add, lets say, 15 more vendors like this, will I have to create methods everytime I add new vendor? Is there any cleaner solution for this?
My controller right now looks like this:
class PaymentController extends BaseController
{
protected $vendorpay_validator, $vendora_validator, $transaction, $transaction_log, $vendor_product, $vendor_transaction;
public function __construct(VendorpayValidator $vendorpay_validator, VendorAValidator $vendora_validator, Transaction $transaction, TransactionLog $transaction_log, VendorProduct $vendor_product, VendorTransaction $vendor_transaction)
{
$this->vendorpay_validator = $vendorpay_validator;
$this->vendora_validator = $vendora_validator;
$this->transaction = $transaction;
$this->transaction_log = $transaction_log;
$this->vendor_product = $vendor_product;
$this->vendor_transaction = $vendor_transaction;
}
}
These four are the Model Repository Objects: $transaction, $transaction_log, $vendor_product, $vendor_transaction
If I have to add more venodrs, it keeps on adding validator object here. What would be a much cleaner way to do it?
One of the solutions that I thought was - for multiple routes, I just create one method. Now I'll check the route in this method and call the Factory Object basis on that.
You should have just one route...
payment/callback/{vendor}
Then if you want to go the factory route (which I think would be a good idea in this case)...
class VendorValidatorFactory
{
private function __construct() {}
public static function getValidator($vendor)
{
switch ($vendor) {
case 'vendorpay':
return new VendorPayValidator;
case 'vendora':
return new VendarAValidator;
}
}
}
Remove the now unnecessary injections from your constructor and in the method which responds to your route, use the factory to grab the correct validator...
class SomeController extends Controller
{
public function __construct(Transaction $transaction, TransactionLog $transaction_log, VendorProduct $vendor_product, VendorTransaction $vendor_transaction)
{
$this->transaction = $transaction;
$this->transaction_log = $transaction_log;
$this->vendor_product = $vendor_product;
$this->vendor_transaction = $vendor_transaction;
}
public function processVendorResponse($vendor)
{
$validator = VendorValidatorFactory::getValidator($vendor);
}
}
And just a suggestion, every time you need to add a new method to your validator classes which your controller uses, add that to a ValidatorInterface and make sure all your validators implement that ValidatorInterface. This way when you need to add more, all you need to do is implement that interface and that should tell you exactly what functions you need to write. Then just update your factory method to include the new one and you are done. No more changing your controller, adding routes, or adding dependencies to your controller.
My goal of asking this question is to ferret out whether there are benefits to injecting Controller directly with the data it needs (more specific approach) opposed to injecting a Model into a Controller (more generic approach). Or to establish whether or not it is just a matter of preference.
Injecting Controller with Model:
Model can be used to run all kinds of queries to retrieve various bits of data, but it is a heavier-weight construct than the data itself. Model essentially contains data, or at least it can access all the data you may need. Example:
class CategoryControllerWithModel
{
private $model;
public function __construct($model)
{
$this->model = $model;
}
// generates HTML for input form
public function genHtml()
{
/* retrieve data */
$categories = $this->model->getCategories();
//...
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
$controller = new CategoryControllerWithModel($model);
return $controller;
}
Injecting Controller with Data:
Here we do a bit more upfront with in the Factory method but we get a leaner Controller that only receives exactly the data it needs and is so completely separated from the Model that it is not even aware of its existence.
class CategoryControllerWithData
{
private $categories;
public function __construct($categories)
{
$this->categories = $categories;
}
public function genHtml()
{
$categories = $this->categories;
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
//a bit more work to get the data Controller needs
//benefit: Controller not tied to the Model
$categories = $model->getCategories():
$controller = new CategoryControllerWithData($categories);
return $controller;
}
Question:
I suppose MVC stands for exactly that -- Model, View, Controller, so injecting Model is probably considered to be an "okay" thing to do. If so, am I taking this too far by trying to remove Controller dependency on Model?
Suppose I insist that I want to inject Data into my Controllers rather than the Model. Is this a purely preferential issue do you see any concrete benefits of doing so?
From my point of view, Factory shouldn't be responsible for domain logic. It should only be responsible for building things up.
In this case, where you are injecting data, Factory has to know what categories controller is searching for, are there any filtering and so on.
So I think for controller you should only inject model, keep Factory single responsibility only for building things and controller should be responsible for it's data.
I think it's a matter of "separation of concerns" also I do not think that would be a good example of using MVC. I would think more along these lines:
class FooController
{
public function actionView($alias){
$category = Category::loadByAlias($alias);
..... load and render layouts etc .....
}
public function actionList(){
$categories = Category::loadAll();
..... etc ......
}
}
like this the neither the Controller nor the Factory need to know what needs to be done when you load a category nor do they have to handle active/inactive status, even User access ... etc this is all Model Logic, Model can have beforeLoad and afterLoad functions, conditions for listing all categories, eager or lazy loading of related models etc...
Take a moment and consider the following Laravel controller:
class UsersController extends Controller {
public function index()
{
// some index stuff
}
public function ajax()
{
// This is how I like to do CRUD
if(Input::get('action') == "edit"){
return $this->edit(Input::get('id'));
}
if(Input::post('action') == "update"){
return $this->update(Input::all());
}
...
}
private function edit($id)
{
// Let's populate an edit users dialog (popup)
$viewData = array();
// Call edit method of my Users service & send that data to view
// (Note: "with" is a Laravel Helper)
return View::make('users.usersEdit', with(new Users)->edit($id));
// Or should I just have the service take care of the view, like this:
// return with(new Users)->edit($id);
}
}
In the spirit of "skinny controllers, fat services"...(my spin on the slogan)...
If I feel very confident that the edit method of my Users service (not shown here) is always going to need it's corresponding view, should I have the view called from the service (instead of calling the view from my UsersController as shown above)?
Your view is not a requirement for your edit user method, it is the output of the result. Users is domain logic. It should ideally have little to no dependency on Laravel, so you could stick it into another framework with little to no modification.
A controller is the perfect place to return Views. Definitely not in your domain logic. :)
This question already has answers here:
How should a model be structured in MVC? [closed]
(5 answers)
Closed 9 years ago.
In the ideal world one should not rely on singletons, the model in the controller and model inside the view would be 2 different instances. The problem arises when the controller sets a state and the view presentation depends on that state. For example:
class MyController extends Controller {
public function __construct(ModelUsers $cModel)
{
$this->model = $cModel;
}
public function action_Search($username) {
$this->model->filterByUsername($username);
}
}
class MyView extends View {
public function __construct(ModelUsers $vModel)
{
$this->model = $vModel;
}
public function users() {
return $this->model->getUsers();
}
}
How to share data between the controller model and the view model?
Starting from basics
A view requests from the model the information that it needs to generate an output representation to the user.
It means the view should be only responsible for showing the information. Just for that. You can also do some things like triming, chaning text size etc. but you shouldn't do some countings there or more complicated operations.
A model notifies its associated views and controllers when there has been a change in its state. This notification allows the views to produce updated output, and the controllers to change the available set of commands.
Model should be responsible for doing data operations. You can use it for example to get the records from database. It just be responsible for data handling.
A controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document).
Controler is kind a proxy between model and view. You get there params and according to this params you set proper action of your controller. This action should create correct model object and use it to get data then assign to the view.
I've never used singleton in models. If you need some classes that would help MVC structure you can use helpers and as Hast suggested Registry pattern. I'm not a fan of using singleton.
You may also want to look at When to use singleton
So your question.
Controler -> model = Passing data via arguments of model's methods
Model -> controler = If reference then just work on it, if argument then do something and return result
Controler -> view = assign proper data to be viewed.
View->controller = go to special url to make data or use ajax request to retrieve it.
You can use Registry or Dependency injection instead.
Also in some cases you may pass some data to your view class as array. Like this:
class MyView extends View {
private $data = array();
public function __construct($data)
{
$this->data = $data;
}
public function users() {
return $this->data['model']->getUsers();
}
}
Of course you have to pass model when you caling the View class from your controller (or wherever you make call).
I am working on building a lightweight MVC, mainly for the learning process but I would like it to be good enough to use eventually.
Below is a basic example/demo of how a basic controller might would look, let's assume the URI has been processed and routed to this controller and these 2 methods.
1) I need to get data from database/cache/etc... inside my Model classes, I just need help on how I should load my models into my example controller below, you can see that I have added this below $profileData = $this->model->getProfile($userId) that is just made up and does not exist's, how could I get something like that to work though? Or should I load the model into the class a different way?
2) A lot of pages will require a user to be logged into the site. SHould I process that part below in the controller to check if a user is logged in, example, before building the profile page, check if user is logged in, if not then build a login page instead and add these checks inside of each controller method/page?
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriendlist();
$this->view->load('userFriends', $friendsData);
}
}
core
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
There are probably tons of ways to accomplish what you are trying.
The "easiest" is probably to just override the constructor and instantiate the model directly.
in User_Controller:
public function __construct(DependencyContainer $dc) {
parent::__construct($dc);
$this->model = new User_Model();
}
I'm guessing that you are looking for something a little more automated though. If you want the Model to have the same name as the controller minus "_Controller", just use get_class($this) in the constructor and use PHP's string functions to parse out what you want. Once you have that in a variable, you can use that variable to instantiate the model:
in Core_Controller:
public function __construct(DependencyContainer $dc) {
$this->view = new Core_View();
// $model_class should be 'User_Model' now
$model_class = str_replace('_Controller', '_Model', get_class($this));
// now instantiate the model
$this->model = new $model_class();
}
I haven't actually worked with any framework that can only have one model associated with each controller (except may CakePHP? I can't remember). With Symfony, the models and controllers are completely decoupled so you can use any model with any controller. You just instantiate the model as need. Symfony use the Doctrine ORM so for example, in a controller action, if you needed a model you would do something like this:
$model = Doctrine::getTable('User');
It might be worthwhile to consider a design more like that in order to promote a decoupled design and I promise that you will want more than one model in some controller at some point.
2.) As far as authentication. Something that seems to be fairly common is to have some sort of setting (whether in a config file or a member variable) that says whether or not the current action needs the user to be authenticated. This is processed each time the action runs (Yii calls these kinds of things filters). If the user needs to be logged in, it stores the page that they are trying to access, and then redirects them to a log in page (you should only ever have to create one). Once they properly authenticate, it will redirect them back to where they were originally heading.