Are CakePHP models actually controllers? - php

I am no expert in the MVC architecture, however I have been working using the mentioned architecture on some JAVA projects. Now I'm getting into PHP MVC Frameworks, (like CakePHP and Laravel) and I decided to go for CakePHP.
Going deep into it, I see Models in CakePHP are in charge of operations like querying and some others while data itself is managed in arrays (i.e. $user['User']['first_name']).
The way I am familiar with, Model classes are just for storing model data, in other words, what arrays do in CakePHP
public Class User {
private String firstName;
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getFirstName() { return this.firstName; }
}
And Controller classes are in charge of querying and other model-building operation, in other words, what models do in CakePHP:
public Class UserManager {
public User getById(int id) { /* */ }
public boolean save(User user) { /* */ }
}
So, are CakePHP, and simillar MVC Frameworks, Models actually controllers? also if yes, shouldn't this be a new type of architecture, something like Router-View-Controller, since controller class actually routes request ?

I think the official documentation explains it already pretty well:
Models are the classes that sit as the business layer in your
application. This means that they should be responsible for managing
almost everything that happens regarding your data, its validity,
interactions and evolution of the information workflow in your domain
of work.
Usually model classes represent data and are used in CakePHP
applications for data access, more specifically they represent a
database table but they are not limited to this, but can be used to
access anything that manipulates data such as files, external web
services, iCal events, or rows in a CSV file.
Actually a model in CakePHP 2.0 is basically thought to do any kind of data manipulation.
Need to turn an array into a CSV string? Model job.
Need to validate an uploaded image? Model job.
Need to save an uploaded file? Model job.
Need to somehow manipulate your POST data? Model job.
...
None of the tasks above is thought to be done in a controller, we want to respect separation of concerns (SoC). The controller just manages the request but doesn't process data, that's the models job. Also models are more easy to unit test than controllers plus you can share them in a shell, you cant do that with controllers without MVC violations: Here is an example:
You should always keep your models fat and controllers thin:
/** CONTROLLER **/
if ($this->request->is('post')) {
if ($this->Model->doSomething($this->request->data)) {
$this->Session->setFlash(__('Something was done right!'));
$this->redirect(['view' => $this->Model->data['Model']['id']);
}
}
/** MODEL **/
public function doSomething($data) {
$this->create();
$result = $this->save($data);
if ($result) {
$result[$this->alias][$this->primaryKey] = $this->getLastInsertId();
$this->data = $result;
return true;
}
return false;
}

No, models are not controllers, not in any php framework out there.
Models just present a collection of your data, a controller at the other hand controls the flow between models and view. For example, the controller calls your model to get data, then process that somehow and push to to the view, so you can pick it up there and do something with it.
Are you willing to tell why you choosed for cakePHP ?

Related

How to structure Laravel applications when not using Eloquent ORM

I'm new to Laravel and am working on a collection of API endpoints that fetch data from a variety of database tables, transforms and processes that data, and then returns it as a JSON response. What I'm struggling to decide, from reading the documentation, is where my processing/transformation code should live.
I've setup my routes in routes/api.php, and have them point towards a Controller subclass, but from here things get a little murky because I'm not currently looking to make use of Eloquent ORM. It seems like typically I'd generate Model subclasses for each database table, and have a Repository call into those and transform the returned data, like so:
Route => Controller => Repository => Model
But where should I be placing both the database query code, and the logic required to process/transform that data (the business logic), when not making use of Eloquent ORM or the Model paradigm? Making the controller fat with DB queries and logic seems like a messy solution, but at the same time a lot of the Laravel DB example code does place logic in Controller subclasses.
So I'll offer an approach that is I think accomplishes what you are asking. In the API I developed, I used the route/api.php file to create the API endpoints. Each of these point to a Controller where the authentication and request validation is performed. The Controller then calls a Service class that handles all the business logic of the application and these are the real heavy lifting parts. The Service class makes calls to a Repository, which actually performs Model changes and saving.
I saw this used in another project several years ago and mimicked it for my projects. The code flow is shown below. Not sure if it will work for you, but I find it to be a very neat and keeps the code grouped together in logical ways.
Route => Controller => Service => Repository => Model
Here is an example of how you can setup your project without Eloquent.
Models are just data containers (records). They don't know how or where to store themselves. They are instantiated with some data and provide access for it:
class OrderRecord
{
protected $id;
protected $createdAt;
public function __construct($id, $createdAt = null)
{
$this->id = $id;
$this->createdAt = $createdAt ?: date('d-m-Y H:i:s');
}
public function getID()
{
return $this->id;
}
...
}
You read and write models only via repositories. Repository is just a class with your typical methods to retrieve/write one or multiple records (find, findAll, update, delete etc). It reads data, instantiates record(s) with it or take record(s), gets data from them and writes it. For example:
class OrderRepository
{
public function find($id): OrderRecord
{
// Here goes your querying. StdClass will be returned in this case
// that will be used to create new record.
$row = DB::table('orders')->find($id);
if ($row) {
return (new OrderRecord($row->id, $row->created_at));
} else {
// If not found you can throw an exception or return null
// (return type should be ?OrderRecord then) depending on
// how you want to design your workflow with repositories.
...
}
}
...
}
Lastly your business logic can be in your controllers. Your controllers should use repositories to get your model objects, work with them and the use repositories to store result if needed.
Obviously, you will need to implement base classes/interfaces for all your entities. Use dependency injection and Laravel's service container to glue everything together.
You can also extend this design by separating querying and mapping in your repository - you will have Repository classes that know only how to retrieve/store raw data and you will have Mapper classes that know how to create/retrieve raw data to/from your records (and serve as factories that repository will use to generate record objects from query results). If business logic also need to be interchangeable then you can also take business logic out of your controllers and put it into separate classes that your controller can utilize.
Here is an excellent article about repository-based approach and how you can extend it.

What should the service layer of an MVC web application look like?

I have been reading about how to make my controllers clean and simple by moving business logic into a service layer. The problem I am encountering is that if I:
Align the service layer towards models e.g. UserService, AuthenticationService, then that reduces redundancy but it also assumes that each method of the UserService e.g. update() is going to be used in the same way but business logic as we know is different in different parts of the application. Methods that serve multiple scenarios are usually dirty because if 3 different parts of the application use the same method then you have to satisfy all 3 parts.
Align the service layer to reflect the controllers and their methods. This way has the potential to increase redundancy but it also makes it so you don't build single golden bullet methods that are supposed to encompass any scenario they're needed.
I have shown 2 examples below:
Reflecting models
class UserController extends Controller {
public function update(Request $request){
// Load user service and create history
$user_service = new UserService();
$user_service->update($request);
// Load user history service and create history
$user_history_service = new UserHistoryService();
$user_history_service->createHistory($request);
// json response with success
return response()->json([]);
}
}
Reflecting controllers
class UserController extends Controller {
public function update(Request $request){
// Load user controller service, update user and create history
$service = new UserControllerService();
$service->updateUser($request);
$service->createHistory($request);
// return json response with success
return response()->json([]);
}
}
The 2 ways I have identified are just what I have been gathering from reading and also a bit of my own thinking. Which way scales properly and I guess has the least sacrifice?

Refactoring controllers

i've some questions on controllers structure for limiting duplicating code.
for example i want to retrieve men and woman. What's the best method to do this:
class User {
public function men() {
//render
}
public function women() {
//render
}
//OR
public function by_type($type) {
//render
}
}
It's a simple example but the number of type can grow. And each type can have seperate views. I'm searching for a scaling solution for the future. A best practice for this case of use.
Thanks
Rails has a (somewhat controversial) principle called fat model, skinny controller, which basically means that you can use the controller to process logic for the views, and let the models handle the "heavy lifting" so-to-speak
CakePHP / Rails
To port from CakePHP to Rails, I would highly recommend looking at using the models as much as possible, as it allows you to create an application which utilizes the full performance structure of the server, and not just leave all the logic in the controllers, as is what many people do with CakePHP
Specifically for your issue:
#app/controllers/users_controller.rb
def index
#user = User.gender(params[:gender])
end
#app/models/user.rb
def self.gender(type)
where?("type = ?", type)
end
This allows you to keep your controller as thin as possible, thus allowing for the correct distribution of code throughout the application
I see Rails as a lot different than CakePHP, in that it helps you create really functional & content-rich applications that utilize the entire server, rather than just providing a layer to make a website dynamic
As far as I understand your question, you could call the function that really render the type inside the by_type function this way:
public function by_type($type) {
if (method_exist($this, $type) {
return call_user_func(array($this, $type));
}
else {
throw new Exception('method do not exists!');
}
}
This way you only need to write the method that render the type and call it using by_type method.

CodeIgniter: Where should a particular functionality go?

Here is a quick overview of the controllers functionality in most of the application:
controller loads a specific model, gets data from it, formats the data and passes the formatted data to the view.
Now there is a search page, which needs to do a search query over entire database (all models). It needs to show each type of data in its particular formatted output on a single page as a list.
The problem:
The search controller can do the search, dynamically load model for each record type, and get the data from model. Problem comes when the data needs to be formatted. I am trying to load the specific controller from the search controller, which is causing problems.
What to do?
PS: I tried using the 'Wick' library, but it fails when the controller's format function tries to use its own model and session object, giving errors about call to a member on a non-object.
After much refactoring and trial/error, It appears that the best way to achieve the above is this way:
Keep the format function in the base controller from which all other controllers are derived. The format options are passed to the function along with the data object as arguments.
Make a static function in each derived controller, which returns the formatting options of the data.
Inside the search controller (which is itself derived from the base controller), for each data object, call the static function of its particular controller which returns the data formatting options, then use that to format the object for the view.
I guess I can say I will stick to using the model only for interaction with the database, and let everything else be done by controller. If anyone has a better solution still, I am all ears.
It sounds like you want to use the Factory design pattern
Make this a library:
class MyModelFactory {
static public function Factory($data) {
$type = key($data);
return new $type($data);
}
}
now, in your controller, you can do something like this:
$model = MyModelFactory::Factory(array($_REQUEST['model'] => $_REQUEST));
and now you have an object of whatever model was specified in $_REQUEST['model']. Be sure to take any security precautions you may need for your application to assure the user has permissions to use the model that they request
Now, since you want to be using common methods and stuff, your models should probably be based off an abstract class / interface.. so instead of
class MyModelOne extends Model {
// stuff
}
You probably want something like this, to ensure your required methods will always be available:
abstract class MyAbstractModel extends Model {
protected $search_params;
public function __construct($data = array()) {
$search_params = $data['search_params'];
}
protected function GetSearchParameters() {
return $this->search_params;
}
abstract public function GetData();
abstract public function GetColumns();
abstract public function DefineViewOptions();
}
class MyModelOne extends MyAbstractModel {
public function GetData() {
$params = array();
$params[] = $this->db->escape_str($this->GetSearchParameters());
// return whatever data you want, given the search parameter(s)
}
public function GetColumns() {
// return some columns
}
public function DefineViewOptions() {
// return some configuration options
}
}
In general you can't load another controller from within a controller in CodeIgniter (although there are mods that allow you to do something like this).
I would try creating a class for formatting your data and add it to the application/library folder. Then load, use and re-use this class throughout your various controllers.
Here is a page from the CodeIgniter documentation Creating Your Own Libraries that explains the details and conventions.
Also, if a class is overkill, creating helper functions is an even lighter approach.
The difference between libraries and helpers in CodeIgniter is that libraries are classes, helpers are just a group of php functions.
Once you have formatted your data, you can load any view from any controller, so you should still have all the re-usability you need so you DRY (don't repeat yourself)
There are a few simple approaches based on the principle of what's simpler (versus what's perfectly DRY). Here's one alternative approach I use with CodeIgniter:
Instead of trying to load multiple controllers, reuse the view fragments from your search controller (or search route, depending which you're using). This requires using the same naming conventions for your data elements so the views are interchangeable, but you should be doing this anyway.
Instead of using multiple models for search, add a single Search model that knows about the things that can be searched on. If you want to prevent duplicate SQL, reuse the SQL between models (this can be done using constants, or loading SQL from disk).
Controllers are not great candidates for reuse from your own PHP code: they route actions and requests for resources to the things themselves. They are intended to be called via HTTP, using the URI interface you've come up with. Calling them from code is a coupling you want to avoid. That said, reusing controllers from JavaScript (or via cURL) is a great, decoupled way to reuse things in any web framework.

Passing data from MVC Controller to View in PHP

I have my own hand-rolled PHP MVC framework for some projects that I'm working on. When I first created the framework, it was in the context of building an admin CMS. Therefore, there was a very nice one-to-one relationship between model, view, and controller. You have a single row in the DB, which maps to a single model. The controller loads the model and passes it to the view to be rendered (such as into an edit form). Nice, clean, and easy.
However, now that I'm working on the front end of the site, things are getting sticky. A page isn't always a view of a single model. It might be a user directory listing with 20 users (each a User model). Furthermore, there might be metadata about the request, such as pagination (current page, total pages, number of results) and/or a search query.
My question is, what is the cleanest way to pass all this data to the view?
Some options I'm considering:
Have the controller create an array and pass that to the view as a single parameter:
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$data = array()
$data['models'] = $models;
$data['currentPage'] = $current;
$data['totalPages'] = $total;
return $view->render($data);
}
}
class UserView{
public function render($data){
// render the data
}
}
Create properties in the view class and have the controller populate them:
class UserView{
public $models;
public $currentPage;
public $totalPages;
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->models = $models;
$view->currentPage = $current;
$view->totalPages = $total;
return $view->render();
}
}
Give the view some sort of generic HashMap or Collection object as a container which can hold any arbitrary number and name of data.
class UserView{
public $collection = new Collection(); // works like a Java collection
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->collection->add($models,'models');
$view->collection->add($currentPage,'currentPage');
return $view->render();
}
}
I know that technically any of the could work, but I'm unsure of the best choice, or if there's a better or more conventional choice that I'm missing.
I'm going to recommend the concept of Fat Models, Skinny Controllers (or, Fat Models Thin Controllers if you prefer...)
In otherwords, your model is too strict - tying your model to represent only something like a RowDataGateway is extremely limiting.
In fact, I think good models hide the fact that you're reading the data from a database at all. Because, in reality, your data could be in text files, or from a web service, or whatever. If you treat your Model like nothing more than a glorified DBAL, you doom yourself to having tightly-coupled code in your controllers that just won't let you break away from the "data only comes from the database" way of thinking.
I've seen both of the first two methods implemented in popular MVC/templating frameworks.
django uses the first method, passing to the view a dictionary of variables which the view uses to fill the template.
smarty uses the second method, creating a Smarty object and assigning values to each the properties in the container.
Your third method seems to essentially be the same as the second, with minor architecture differences.
Really, I guess I haven't said anything that you haven't thought of already. Basically, these are all sounds ideas, so implement whatever you feel you are most comfortable with.
In the one I use, it has automatically has a view property in the controller that you can access methods and properties on the view. All public properties are then accessible within the view view '$this' since the view is rendered in it's own objects context.
In the controller:
$this->view->myVar = 'test';
And in the view:
$this->myVar; // 'test'
The same goes for the layout since the are both separate instances of the same view object:
$this->layout->myVar = 'test';
And then in the layout:
$this->myVar; // 'test'
The framework used to be proprietary, but is about to be released to the general public. I'd be happy to send you some code from it if you think that'd help. Remember, the simplest answer is usually the best answer.

Categories