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.
Related
I'm asking myself if i'm doing things in the right way. Here is my concern :
I have my User Model like this
class User extends Model {
public function activities()
{
return $this->hasMany('App\Activity');
}
....
public function getTotalDistanceTraveled() {
return Step::join('activities', 'activity_id', '=', 'activities.id')->where('activities.user_id', $this->id)->sum('steps.km');
}
Is it the right place to put function like getTotalDistanceTraveled() in the User Model ? Or it should be in the Controller which generate the View ? Or do I have to create a UserRepository ?
Thanks for your recommandations
For me it depends on the size of the app. If it's a simple app that is going to take very little maintenance and basic Eloquent queries then sure keep them in the model - what's the point in troubling yourself over a small app?
If your app is going to be constantly maintained and large in size then I would result to a Repository design, that way you keep everything organised, maintenance is easier and anybody else working on the project with yourself can find their way around relatively easier.
Personally on a large app I like to have repositories but split my more advanced, complex queries into a dedicated class for each query and remove them from the repository. This for me makes maintaining easier.
Controller wise, I personally prefer to keep my controllers 'skinny'. But a simple Eloquent query isn't going to hurt anything for example:
public function view($id)
{
return view('view-user')->with('user', User::find($id))
}
It ultimately depends on the scope of your app.
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 ?
I am just posting this question so some of you might be able to point me in the right way. I am slowly warming up to OOP, starting to understand the concept. I want to make a good solid core or foundation to be used as a CMS backend. It will also use MVC. I have been using http://gilbitron.github.com/PIP/ as the MVC- base.
The thing I cant figure out is the following:
Say, on the projectpage in the backend I have 2 sections: htmltext and projects and I should be able to edit them both. The uri would be something like:
//domain/backend/projects (the method would be the index and show the 2 sections)
When i click on projects how should it be handled?
//domain/backend/projects/projects/ or
//domain/backend/projects/list/
One step further, a project will hold some images or a gallery:
//domain/backend/projects/edit/5/gallery/2
My question here is, first: would this be a good way to go and even more important how would this be implemented in OOP
the main projects controller:
class projects {
function index(){
// view index
}
function edit{
$project = new Project();
$projectdata = $project->load(5);
}
}
A single project controller
class project {
function __construct(){
$this->projectmodel = $this->loadModel('project_model'); // prepare the model to be used
}
function load($id){
$this->projectmodel->loadproject($id);
}
}
project model
class project_model extends model { //extends for DB access and such
function __construct(){
// do stuff
}
function loadproject($id){
return $this->db->query("SELECT * FROM projects where id=" . $id . " LIMIT 1");
}
}
Now my question. If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model
function loadimages($id){
$this->images->load($id);
}
and then images would be something like:
class image extends model { //extends for DB access and such
function __construct(){
}
function load($id){
return $this->db->query("SELECT * FROM project_images where id=" . $id . " LIMIT 1");
}
}
It seems controllers and models gets mixed up this way. Yet in a logical way a project is a container that holds projectinfo, which could be text, images and maybe video's. How would I go about to set that up logically as well.
The original question
The first part, about the URLs is something called: Routing or Dispatching. There is quite good article about it in relationship with Symfony 2.x, but the the idea behind it is what matters. Also, you might looks at ways how other frameworks implement it.
As for your original URL examples, galleries will be stored in DB. Won't they? And they will have a unique ID. Which makes this, /backend/projects/edit/5/gallery/2 quite pointless. Instead your URL should look more like:
/backend/gallery/5/edit // edit gallery with ID 5
/backend/project/3 // view project with ID 3
/backend/galleries/project/4 // list galleries filtered by project with ID 4
The URL should contain only the information you really need.
This also would indicate 3 controllers:
single gallery management
single project management
dealing with lists of galleries
And the example URLs would have pattern similar to this:
/backend(/:controller(/:id|:page)(/:action(/:parameter)))
Where the /backend part is mandatory, but the controller is optional. If controller is found , then id ( or page, when you deal with lists ) and action is optional. If action is found, additional parameter is optional. This structure would let you deal with majority of your routes, if written as a regular expression.
OOP beyond classes
Before you start in on using or writing some sort of PHP framework, you should learn how to write proper object oriented code. And that does not mean "know how to write a class". It means, that you have to actually understand, what is object oriented programming, what principles it is based on, what common mistakes people make and what are the most prevalent misconceptions. Here are few lecture that might help you with it:
Inheritance, Polymorphism, & Testing
Advanced OO Patterns (slides)
Unit Testing
The Principles of Agile Design
Global State and Singletons
Don't Look For Things!
Beyond Frameworks (slide)
Agility and Quality (slides)
Clean Code I: Arguments
Clean Code III: Functions
This should give you some overview of the subject .. yeah, its a lot. But is suspect that you will prefer videos over books. Otherwise, some reading materials:
PHP Object-Oriented Solutions
Design Patterns Explained
Patterns of Enterprise Application Architecture
You will notice that a lot of materials are language-agnostic. That's because the theory, for class-based object oriented languages, is the same.
P.S.
Be careful with extends keyword in your code. It means "is a". It is OK, if class Oak extends Tree, because all oaks are trees. But if you have class User extends Database, someone might get offended. There is actually an OOP principle which talks about it: Liskov substitution principle .. also there is a very short explanation
If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model
Yes. The goal of the model is to encapsulate business logic so that you only need to write the algorithms once, and share them multiple times. What I would do if I were you is to add a getImages() method to the Project model as the images are a direct attribute to a project, this makes it so that each project knows how to retrieve it's own images.
Your code is missing a few key concepts compared to ORMS I have worked with (hydration and peer/table) classes, so I will try to stick with your code. You can retrieve the images through the Project object, then reference the images via the view 1 of 2 ways:
// Controller
$this->project = new Project();
$this->projectImages = $project->getImages(); // Implemenation #2
// View
// Implemenation #1 (reference to object initialized in controller)
<?php foreach ($project->getImages() as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>
// Implemenation #2 (reference to separate variable initialized in controller)
<?php foreach ($projectImages as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>
Above is demonstrative only, but should give you an idea. I would suggest that you take a look at Doctrine. It is a proven and stable ORM which provides much of what you are attempting to write, plus it adds the missing components that I mentioned earlier; hydration, object relations, and Table classes, along with many other features such as nested sets.
(since the question was long enough by itself I didn't want to extend it so I've added this as an answer)
projects.php
this controller extends the project controller. the main purpose is to handle the multiple getAllProjects() using its own model and then pass it on to the project controller getProject()
class Projects extends Project{
public function __construct()
{
parent::__construct();
$this->projects_model = new Projects_model();
}
public function getAllProjects()
{
$res = $this->projects_model->getAllProjects();
if($res) foreach($res as $v) $projects[] = $this->getProject($v);
return $projects;
}
}
project.php
the project_model does nothing more than getting projectdata and related data like images
class Project{
public function __construct()
{
$this->project_model = new Project_model;
$this->images = new Images();
}
public function getProject($data=NULL)
{
if($data==NULL) $data = $this->project_model->loadProject($data['id']);
$images = $this->project_model->loadProjectImages($data['id']);
return array('project_id' => $data, 'project' => $data, 'images' => $images);
}
public function getProjectByID($id)
{
$data = $this->project_model->loadProject($id);
return getProject($data);
}
}
The upside of this approach, I can use the project class on it own and it will work. I can encapsulate it in projects and it can be used as well.
Is this a good (and the right) approach to do stuff? Or would it be better to put this all together in one controller and one model class?
I am trying to follow good practices as much as possible while I'm learning using OOP in an MVC structure, so i'm turning to you guys for a bit of advice on something which is bothering me a little here.
I am writing a site where I will have a number of different forms for members to fill in (mainly data about themselves), so i've decided to set up a Member controller where all of the forms relating to the member are represented as individual methods. This includes login/logout methods, as well as editing profile data etc. In addition to these methods, i also have a method to generate the member's control panel widget, which is a constant on every page on the site while the member is logged in. The only thing is, all of the other methods in this controller all have the same dependencies and form templates, so it would be great to generate all this in the constructor, but as the control_panel method does not have the same dependencies etc, I cannot use the constructor for this purpose, and instead I have to redeclare the dependencies and same template snippets in each method. This obviously isn't ideal and doesn't follow DRY principle, but I'm wondering what I should do with the control_panel method, as it is related to the member and that's why I put it in that controller in the first place.
Am I just over-complicating things here and does it make sense to just move the control_panel method into a simple helper class?
Here are the basic methods of the controller:
class Member_Controller extends Website_Controller {
public function __construct()
{
parent::__construct();
if (request::is_ajax())
{
$this->auto_render = FALSE; // disable auto render
}
}
public static function control_panel()
{
//load control panel view
$panel = new View('user/control_panel');
return $panel;
}
public function login()
{
}
public function register()
{
}
public function profile()
{
}
public function household()
{
}
public function edit_profile()
{
}
public function logout()
{
}
}
What I would do, is to avoid shoving everything in one controller, and instead separate functionality accordingly - for example, you could have a Registration_Controller to deal only with members' registration, Authentication_Controller, Profile_Controller, and so on, this way is easier to visualize what each part of your application is responsible for, instead of having one single controller with lots of responsibilities, which leads to confusion and other maintainability issues, at least IMHO, it has worked for me.
Going back to your concrete question about the control panel, yes it makes more sense to take it out of the controller, specially if is not an action of it, and as you mentioned you can have a helper class for all the repeatable display logic. If something doesn't make sense within the context of the controller, take it out.
There are some cool frameworks such as Cake and Zend FW, that make life easier at designing/developing MVC application, and that come with a rich set of components that work out-of-the-box.
MVC and Fw's are not mandatory though, it all comes to the scope and your special needs, some times they are of great deal help, most of the times I use them, but sometimes overcomplicate things. Keep it simple ;)
Cheers,
M.
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.