Simple PHP MVC framework design - php

So, for weeks now I have been playing around with my own PHP MVC Framework, purely for learning purposes. I've got the basics done, and I think I know what goes where, etcetera. But I have so far not been able to figure out the following.
Say I have a music database, and I have a "music controller", a "music model" and obviously a "music view". What I want to end up doing is, being able to ofcourse insert a new artist, and/or a new track, also I would like to be able to edit them, and so on. My URL's look like this:
example.com/controller/mainfunction
In my "music controller" I have a method as follows:
public function addTrack()
{
if (isset($_POST["submit_add_track"]))
{
// call the model to add track to database etc..
}
}
So, if I were to have a form in my "music view" and submit that, I could add a new track to the database. I also have a method to add a new artist, and so on.
But here is what I am struggling with. How should I design this? Should I use the music controller to add tracks and artists? Like so:
example.com/music/add/artist
example.com/music/add/track
Or should I do it differently, maybe I should use the "music controller" just to display the artists and tracks, and insert stuff with another controller? Like so:
example.com/insert/artist
example.com/insert/track
The latter seems the way to go for me, but I am not sure if this would be good design.
I think maybe what I am asking is, would it be good to use an "insert" controller, and then a "track method" to display the "insert track" form in the view? Next to this, there will be a lot more pages, not only music. But I just cannot figure out the logic behind this on how to do this properly so I can easily extend.
As you can see, I'm lost, any pointers (big or small) would help me a lot. Thanks in advance!!
Edit:
So, just to see if I'm on the right track now. I have a music controller with the code pasted below. If in the controller the "addArtist" method get's called, it goes to the model, adds the artist to the database, returns something to the controller, and it's done. Now for the part where I still have troubles. How do I display the form to actually add an artist? I know it should be in the view. But how should I call it? Something like this?
example.com/music/insertArtist
This could work, but then I would have an "insertArtist" method, for just displaying the form, and an "addArtist" method, for actually inserting the new artist to the database each time the form is submit. I know I am missing something, but I cannot quite figure this out still. What I am thinking is, in the "addArtist" method, I include the "addArtist" view file to be displayed, which holds the form, and I put in some "if submit then add the artist". Does that make sense and if so, am I on the wrong track?
public function addArtist()
{
// $artist_model->addArtist();
}
public function editArtist($artistID)
{
// $artist_model->editArtist();
}
public function deleteArtist($artistID)
{
// $artist_model->deleteArtist();
}
public function addTrack()
{
// $artist_model->addTrack();
}
public function editTrack($trackID)
{
// $artist_model->addTrack();
}
public function deleteTrack($trackID)
{
// $artist_model->addTrack();
}

example.com/music/addArtist
example.com/music/addTrack
You must strictly follow the format of /controller/method
When I say method, I mean literally, method. In fact, in your base controller class, the way you should call a method is
/**
* Executes the requested action
*/
public function ExecuteMethod($method)
{
$this->{$method}();
}
where you would pass addArtist or addTrack to the method.
Basically, the /controller/ part just tells apache which actual controller class to construct, and the /method/ tells it which method in that controller to execute.
EDIT:
This is the best MVC tutorial I know of. I based my MVC knowledge largely on this. However, it is okay to variate the standard MVC structure a bit to suite your needs better, as long as you do not change the core values of MVC that make it so powerful.
EDIT 2: (reply to your edit)
So what you're asking it how to view the form.
Viewing the form and submitting the form are two entirely different actions, and should be treated as such. Additionally, EVERY controller action, MUST have it's own view.
An easy way to put it, every time a user needs ANY data (or action) from the server, you MUST have an action and a view for that.
What this means, is that you will have two separate methods in your controller, viewForm() and submitForm(). Now, what you showed in your controller class, where you had addArtist(), editArtist(), etc, is inefficient, as you are just aliasing them to their corresponding model functions, which sort of skips the entire point of MVC.
I can't really tell you exactly how to implement the controller, as I do not know exactly what your goal is. But let's assume that you have a page that lists all the Artists, and you have buttons next to each for all the actions (Btw, as I said in the comments, in an implementation like this, if you are displaying Artists and Tracks on different pages, they should really have separate controllers). In this implementation, you would have a controller -> view setup like this:
/Artists/view -> returns an html list of artists with buttons
/Artists/viewAddForm -> returns a blank form you would use for creating new artists
/Artists/submitAdd -> returns an html confirmation page
/Artists/viewEditForm -> returns a form for editing with prefilled values
/Artists/submitAdd -> returns an html confirmation page
/Artists/delete -> returns an html confirmation page
So a user is presented with a list of artists. They have 3 options.
They click add, fill out the form, submit it, and get taken to a confirmation page.
They click edit, edit the form, submit it, and get taken to a confirmation page.
They click delete, and they get taken directly to a confirmation page since there is no data to fill.
The structure for Tracks would be exactly the same.

The way I handled this in my framework was to try and support a RESTful interface. I have
example.com/artist
example.com/track
I then use the HTTP verbs to represent the intent (POST/PUT/DELETE/GET)
It doesn't always fit neatly, but it's one way of doing it.
In your case I would worry about putting too much code in a single file. If you put all the responsibility in an insert controller, that quickly becomes an unwieldy code file. In your case I would probably go with two controllers and then create the right methods on each.

Related

How to integrate menu/footer/menu in PHP MVC

i know that this question already has been asked. But i still dont understand it.
Right now im trying to understand how to make use of the MVC Modell.
But i dont get how to integrate everything and right now I am hesitating to use a framework before i really get it.
What I try to achieve:
I want to have a header, a footer and a menu which remain the same all the time.
Like this:
HEADER
MENU
{CONTENT}
FOOTER
So my thinking is:
My Controller gets some Information, lets say its a User-ID.
Controller calls a method from the Model:
Get all DATA from USER with ID: 1
Then the controller passes the DATA into a view, lets say a list.
So where to go from here?
Should the controller pass this view into another view?
Like:
$site = new View(); <br>
$site->wholeSite($content);
and then site is something like:
HEADER
MENU
{$content}
FOOTER
Please excuse my simple approach, im just trying to get the basic idea behind it and i already read the first 20 pages of googling it.
Just cant get my head around it.....
It would be really nice if you would explain it for an Beginner :)
Thanks in advance!
you can call multiple views from the control like:
$this->load->view('header',$data);
$this->load->view('menu',$data);
$this->load->view('content',$data);
$this->load->view('footer',$data);
If you have a nested div then you can use regular include function like from a particular view file.
include('footer.php');
According to the path of the view file. The data also gets transferred from parent view to the included file.
Generally MVC apps have a universal layout view which contains the header, footer and menus. Sometimes data required for that is handled outside the controller, in say your init script or bootstrap.
An MVC purist might pass all data the layout requires from the controller, but that can be repetitive, although setting the data in an abstract controller construct might alleviate that.
On the question of views and sub-views there are generally two solutions:
A.) You have one principle view which all required data is passed to. In that view sub-views (partials) are called, and data is passed down into them.
B.) You instantiate all your views in the controller, passing all the data they need directly into the view, you would then pass views as parameters into other views.
Both methods have their pros and cons. I find the first option leads to less Fat Controllers, so I usually go with that.
I'd recommend you learn an existing MVC framework ( CodeIgniter, now defunct is basic enough to make it a good entry point), as that will show you how things are done.

Where is the best place to put application-wide logic (notifications) in CakePHP?

I've had a quick search on Stackoverflow but not really found an answer to a question that I have. Sorry if it's already been answered and I've missed it!
I'm working on an application, and similarly to Facebook, there will be 'notifications' but in my case it'll be items that require actions from the user. It would just be a 'count' of the actions that are required and nothing else.
I've followed a fat model, skinny controller approach through my application and so far so good. I have a good idea of what goes where, and when to use a plugin, app model, app controller, etc.
However, I'm a little confused about where to put this notification/action 'count'. I'm guessing either afterFilter() in the app_controller.php file, but that's just a guess. I'm just looking for the most efficient place to put it.
So, where's the best/fastest/most efficient place to put this count call?
Thanks!
Kingsley
EDIT: I just realised it's worth pointing out that this particular call is on a model. So, at the moment I have to load that model, and then call it.
The ideal place is in an Element. You can then use requestAction() to tell it where to retrieve it's data from.
The Element is placed into a view (or used to display a Flash message). Within it, it pulls it's data (requestAction) from a Controller's Action. That action get's it's data from the Model of your choice just like you would in any other action. This allows complete separation of concerns (ie MVC).

Should I have one view for this? Is 2 views alright?

I am trying to program in MVC architecture.
So, I have one HTML form where user can insert, let's say, a movie. I have one controller for that (for adding that movie) and one view for the HTML form.
But I also want user to be able to edit that movie after he added it. So, he presses a button "Edit a movie" and he's redirected to the new URL and I have new controller and new view (it has the same form as when user adds the movie, but only now he sees values in inputs which he entered previously, I hope you understand what I mean) for this editing form.
Also, when I want to show how his movie looks like to other users, I once more time have new Controller and new View for that.
edit: I also have to validate what he enters. So, that validations should be in model? Because I validate twice, it doesn't seem right.
Is it correct thinking of MVC? Or what's the best approach for making this? Thanks.
You got it almost right, but there is still some place to simplify it. Common thing is to create action functions inside of your controller which handle certain (surprise, surprise) actions user can do. Usually you'd have, for example, Article controller with actions add, remove, edit etc. This way you centralize actions for common entity of your application and prevent overflow of controllers. It's easier to maintain, easier to find if you want to change something asap and you will nicely follow DRY principle.
Another thing you could do is to create abstract base controller for common stuff that's used in multiple controllers (dynamic loading of meta data from database comes in mind).
Having multiple views is fine. You don't have much of a choice anyway. But I'd recommend using some templating engine which would make your life easier and once again force you to not repeat yourself. Twig or Smarty would be perfect choice.
Validation logic should be located in model. Model is responsible for most of the backend logic (data manipulation, its validation...). Controllers just take requests, loads appropriate data from Models and point you to proper View. Don't get confused though, you usually end up validating your Models inside Controller (calling validate() functions, for example).
Anyway, in the end, you'll find out there are quite many ways how to look at MVC pattern. Some people prefer "fat models" and "skinny controllers", some the other way around. Use whatever fits your needs and keep it simple!
If you want some studying materials, take a look at Symfony2 framework or CakePHP tutorials. There are some valuable information regarding this topic. Maybe you'll end up using one of them instead of reinventing the wheel :)
No, it is overly complicated.
You do not need different controller for another action you want to make on something. Eg. if you have a controller for creating a movie, you do not need separate controller for editing it. You need separate action, not controller.
Basically you should employ DRY (Don't-Repeat-Yourself) rule for that. If view only differs by the values and form's action, just use one view for both actions (create & edit). View should generate similar code for both actions - one will have no form fields filled, with form's action set to eg. "movies/create" and the second one will have prepopulated form fields with form's action set to eg. "movies/<movie_ID_here>/update" (naming convention is up to you).
Validation should be in model, but as additional help for the users you can validate it also on the client side, using JavaScript or HTML5 (depending what you want), so they do not need to submit the form to know they forgot about something.
Using a typical project layout for me, I might have something like the following:
Controllers
| |
| - MoviesController.php
|
Models
| |
| - Movie.php
Views
| |
| - Movies
| |- Create.php
| |- Edit.php
| - Partials
| |- _MovieForm.php
MoviesController contains two actions - Create() and Edit($id), which have their own views. I personally would have a third action as well, which handles the form submission from both actions:
public function Save()
{
//...snip...
}
Create() simply loads the corresponding view. Edit() is slightly different in that it retrieves an existing record from the database first, using our Model, before passing it to the view.
The two views have a nested "partial", which is a fragment of commonly used HTML - in this case, _MovieForm.php, which has our form inputs (which are capable of displaying passed-in values).
The form also contains a hidden input field for our row id. This is only given a value when we call the Edit() action.
The form submits to the Save() method of MoviesController, which checks if we have a passed row id. If so, get our row from the database (again, represented by our Model), update the values, and call our Model's Save() method.Model::Save() runs our validation logic on our data and either saves to the database, or passes the data back along with our validation error message to the view.
Hope this helps :)

MVC or CodeIgniter limitation: how I can access a 'sub' controller?

I don't know if I'm wrong. But all I know is that in MVC, is that the Controllers is always responsible for calculating the data, and the View to print them.
The problem
I have a poll that I need use a model to get your data and I use a view to print. Currently I can access this poll by use the url "poll/last". This works fine.
My problem is that I need print this info on some pages (like in "site" controller).
The Dilemma
If I simple load the poll view on page, nothing data is get from model. Onetime that it is work of Controller.
If I move all controller part to view, this works fine. The low point is that turn the application in a "non-MVC compatible".
The Solution
So how can I solve this dilemma?
Actually the CodeIgniter not is a HMVC, and the HMVC module don't works fine -- only locally.
There are some viable solution to solve this problem?
Controllers are not always responsible of all calculations. When those calculations are part o fthe 'business or data model' they should go into the model. My english is not good sometimes but i'll try to explain with an example: Let's say we have a table with persons data, and a column birth_date. Age() function should be in Model, because is another way of seen birth_date.
In your case, I would try to move calc into the model, and write a partial who shows the result, and pass the resulting partial view to the main one. Something like
$data['poll_view'] = $this->load->view('poll_partial',$this->poll_model->getPollData(),true);
$this->load->view('current_view', $data ); //that includes poll subview
I may be misunderstanding your question, forgive me if I'm wrong. I can't comment yet on posts, so consider that this may be an answer.
You're wanting to use the data that is in the poll/last controller in another one?
Why not have the same code that you're doing on the controller, in a method/function in the model?
That way, when you call your model just like in the poll controller, the same code and data is available to the site controller.
That's the main function of models.
In my opinion it goes like this:
View - Displays the data, formats the data, and puts the data where you want it.
Controller - Takes the data and determines what exactly to show and on what URL / location to show it, and in which view.
Model - Gets the data from the database, does any calculations that you need to, and returns it.
Hope this helps!
It sounds like you are trying to load the poll view into other views? If so, take a look here: http://codeigniter.com/forums/viewthread/189935/
The same question has been asked/answered
Forgive me if that wasn't your question... can't comment on posts...

Can I call a Model from a View?

Rather than use a full-blown PHP MVC, I'm designing one that will best-fit my uses. I have the basic framework done, and have coded the models and controllers I'll need to run my website.
Now I'm moving onto the Views, and I've encountered a small dilemma. My approach is working fine for me, but for future reference, I want to know if what I'm doing is a bad habit to get into.
What I'm trying to do:
In my View, I'm calling a Model that runs my authentication system, and requesting the login status of a user. I then use that boolean to decide whether to show certain elements within the view, and where to place others.
Should I be designing separate views for each login status, or is this approach fine? However, if I'm going to be implementing this MVC into the work I'm doing for my clients, I need to use the best practices.
Any advice would be appreciated!
Can I call the model from the View?
Yes, you can. As long as you maintain the separation of concerns between M,V and C, you are free to call upon the Model (or the Controller) from the View. Most MVC diagrams show a bidirectional connection at least between View and Model. What you don't want to do though, is place logic/code from the Model (or the controller) into the View and you don't want to modify the model from there.
For example, you might have a widget on your page that aggregates the latest ten blog posts headlines from your favorite blogs on each page of your website. You get the headlines by calling, say MyFavFeeds::getLatest(); in your model. What are your options now?
You could add the code to fetch the headlines into the controller, but that would require you to replicate it in each and every controller action, which is against the DRY principle. Also, the controller's concern is handling user input for specific actions and fetching the headlines on each call is likely not even related to these actions.
If your architecture supports it, you could fetch that data in some sort of preDispatch hook, that is, the headlines get loaded and injected into the View from a plugin or callback. That would be DRY, but a second developer might not be aware of that plugin and accidently overwrite the variable holding the headlines from his controller action. And there might be cases in which you wouldn't want to load the headlines, e.g. when just rendering confirmation pages for form submissions, so you'd have to have mechanism for disabling the plugin then. That's a lot to consider.
You place the call to (not the code of) MyFavFeeds::getLatest() into the View or Layout template or, better, a ViewHelper, that encapsulates the call to your model class and renders the widget. This way you don't have to worry about overwriting any variables or repetition. And when you don't need the headlines on your view, you simply don't include it.
About your other question:
In my View, I'm calling a Model that
runs my authentication system, and
requesting the login status of a user.
I then use that boolean to decide
whether to show certain elements
within the view, and where to place
others.
Authentication is something you will want to do early in the application flow, before any controller actions are called. Thus, you should not run your (entire) authentication system in the View. The actual authentication is not View-related logic. Just requesting the user status after authentication, on the other hand, is okay. For instance, if you want to render a widget showing the user name and giving a login/logout button, it would be fine to do something like
<?php //UserHelper
class UserMenuHelper
{
public function getUserMenu()
{
$link = 'Logout';
if(MyAuth::userHasIdentity()) {
$link = sprintf('Logout %s',
MyAuth::getUsername());
}
return $link;
}
}
If you got larger portions of your GUI to be modified by a User's role, you might want to break your View apart into partial blocks and include them based on the status, instead of writing all the HTML into a View Helper.
If you are only looking to render a navigation based on the user role, have a look at Zend Framework's Zend_Navigation and Zend_Acl to see how they do it.
You can, but you shouldn't. Except for a few extreme cases (and branching your view based on logged-in status is definitely not an "extreme case"), it's pretty much always A Bad Idea to call model stuff from a view.
What you probably want to do in your situation is pass the boolean to the view through the controller. That way, if you change something about the User model, the view doesn't have to know, so long as the controller keeps the behavior the same.
While I only know enough about MVC to get myself in trouble, I've always lived by the fact that unless your view is STRICTLY user interface, then it shouldn't be there.
Also, I've also ran with the idea of thin controllers, fat models.
Going off of these, I'd suggest adding a method to your authentication system model that returns the appropriate view to render and passes that to the view.
Okay, I would really try to keep my Views as logic-free as possible. If you need to switch anything based on e.g. the result of a model method, put a controller in place that delegates the rendering.
Just make sure you follow the basic idea: Do your stuff in the models, Tell your models what to do from your controllers and also tell your views what to show from your controllers.

Categories