I am trying to build a nice PHP framework for personal use. I realize there are many existing but this is a great learning experience that covers a vast majority of different challenges and really teaches me a lot, as well as having a finished product when i'm done that I can hopefully use to develop other projects from and since I am building it, there should be no learning curve on how to use it.
Some of the basic goals,
- Use PHP Object Oriented instead of procedural.
- Use an MVC or something similar to learn more about this style.
- Be lightweight and fast/good performance
Here is my planned site structure, excluding some other folders for javascript, images, css, some helper functions/files, etc.
///////////// Site structure /////////////
site.com/
/index.php
site.com/library/
/Config.class.php
/Photos.class.php
/Mail.class.php
/Filter.class.php
/QRcodes.class.php
/Router.class.php
/Database.class.php
/Templates.class.php
/etc, etc,etc......
site.com/modules/
/account/
/model
/views
/controllers
/users/
/model
/views
/controllers
/messages/
/model
/views
/controllers
/API/
/model
/views
/controllers
/forums/
/model
/views
/controllers
/blogs/
/model
/views
/controllers
/etc, etc, etc, etc.............
/model
/views
/controllers
I have decided to route all Request through a single point of entry, index.php
I will build a Router class/object that will match the URI against a map of possible destinations using regular expressions. Here is a snippet of what I have for now for this part...
<?php
//get url from URL
$uri = isset($_GET['uri']) ? $_GET['uri'] : null;
$uri_route_map = array(
//users/account like http://mysite.com/users/324 (any digit)
'users/friends/page-(?<page_number>\d+)' => 'modules/users/friends/page-$1',
'users/friends/edit/page-(?<page_number>\d+)' => 'modules/users/friends/edit/page-$1',
'users/friends/edit' => 'modules/users/friends/edit',
'users/friends/' => 'modules/users/friends/',
'users/online' => 'modules/users/online/' ,
'users/online/page-(?<page_number>\d+)' => 'modules/users/online/page-$1',
'users/create' => 'modules/users/create',
'users/settings' => 'modules/users/settings',
'users/logout(?<page_number>\d+)' => 'modules/users/logout',
'users/login' => 'modules/users/login',
'users/home' => 'modules/users/home',
//forums
'forums/' => 'modules/forums/index',
'forums/viewthread/(?<id_number>\d+)' => 'modules/forums/viewthread/$1',
'forums/viewforum/(?<id_number>\d+)' => 'modules/forums/viewforum/$1',
'forums/viewthread/(?<id_number>\d+)/page-(?<page_number>\d+)' => 'modules/forums/viewthread/$1/page-$2',
'forums/viewforum/(?<id_number>\d+)/page-(?<page_number>\d+)' => 'modules/forums/viewforum/$1/page-$2',
// TESTING new method to define class and page better!
'users/home' => array('PAGE CLASS NAME', 'ACTION NAME')
//blog routes coming soon
//mail message routes coming soon
//various other routes coming soon
);
//////////////////////////////////
class Router
{
public function __construct()
{
}
public function get_route($uri, array $uri_routes)
{
foreach ($uri_routes as $rUri => $rRoute) {
if (preg_match("#^{$rUri}$#Ui", $uri, $uri_digits)) {
//if page number and ID number in uri then set it locally
$page_number = (isset($uri_digits['page_number']) ? $uri_digits['page_number'] : null);
$id_number = (isset($uri_digits['id_number']) ? $uri_digits['id_number'] : null);
echo '<hr> $page_number = ' . $page_number . '<BR><hr> $id_number = ' . $id_number;
$uri = preg_replace("#^{$rUri}$#Ui", $rRoute, $uri);
echo '<BR><BR>Match found: ' . $uri_routes . '<BR><BR>';
break;
}
}
$uri = explode('/', $uri);
}
}
$uri = new Router();
$uri = $uri->get_routes($_GET['uri'], $uri_route_map);
?>
PLEASE NOTE
THE CODE ABOVE IS ALL TEST CODE AND WILL BE CHANGED, IT IS JUST THE CONCEPT
So as you can see I am planning to have index.php get the URI, check it against valid paths, if one is found, it will include or build a header section, then will build the content section, then finally the footer section of the page.
If you were to access for example... www.test.com/blogs/userid-32423/page-23
The the page would...
build header()
create object blogs... $blogs = new Blogs;
call $blogs->viewbyID($userID,$paging); //$userID would be 32423 and $paging would be 23 from the URI
build footer section
Now based on my folder structure. I believe that the blogs class file in our above example would be considered the CONTROLLER. If I am correct so far, then this blogs class which is calling blogs->viewbyID(ID,PAGE) the viewbyID method would set up some code, query the database and set up some variables for the page and then it could include a blogs template file. This blogs template file could be considered the VIEWS.
Now I might have this whole concept wrong, and that is why I posted so much code and text to try and explain my outlook on it, please give me thoughts, suggestions, tell me where im completely wrong, and where I might be on the right track, I will greatly appreciate any constructive critism or thoughts. If I am right in my above usage of the View, Controller portion of the MVC pattern, then what part of my code would be considered the Modal? This is somewhat confusing to me for some reason.
Bonus question... What about form post, where should I process these at? In my example I am focusing on the blog module, so lets say POST for adding new blog entry and POST for editing blog entry, where should these be processed (modal, view, controller)?
The controller's job is to examine the user's input and determine what is being requested. Once that's determined, the model(s) is invoked. The controller then takes the model's payload and gives it to the view.
Basically, the model is a model of the business. Want to add a blog post? Then the Blog model will have a ->add or ->save method (which is called by the controller. The blog controller may also have an add method, but it is not for talking to the database. It's for examining the input and then calling the model to do the actual saving). Model methods don't always interact with a database but they usually do.
As far as add/edit, they almost always share the same view and can share the same controller methods if doable.
Just remember that the controller is the entry point for all your clients. Every URL your application handles should map to a controller method. The controller then tells the model what to do, passing the user input to it.
Your Models should be the one querying the database, not your Controller. Your Model is there to handle all CRUD actions and, where necessary, pass results back to the Controller to hand to the View. Usual flow would be
Controller > Model > Controller > View
Related
I've just started to get into MVC with PHP and have had a good mess about with the likes of CodeIgniter and CakePHP. I'm interested to find out what people's approaches to the following would be:
Normally when I have built a website with a CMS in the past I have approached it by having a core URI table in my database. Each URI is unique and represents a page on my website (e.g. www.example.com/about would reference a record in my URI table with 'about' as the unique URI). The URI table also contains a 'type' column which tells the system what type of page it is (e.g. splash, basic, gallery or whatever). Each 'type' has a corresponding table in my database with all the data for records of that type in them (e.g. I would have tables: basic, gallery and splash). The type also tells the system which template/pagehandler to load which in turn does what it needs to do for each page type.
So if you go to www.example.com/about, my system looks in my URI table for a record with URI 'about', finds it's type to be 'basic' so it loads the basic template/pagehandler which uses the basic table in my database to load and render the page. In the CMS I follow a similar approach, I'll have add/edit forms for all of the different types in my page manager.
I was wondering how you would approach this using an MVC framework such as CodeIgniter? I essentially want to have different controllers for each type of page on both the front and back. However, when someone get's to my site, they will end up on a URI with a single level so I need to check the type of the page and pass off to the correct controller. Is there a way you would recommend of checking the type of each page and then loading the relevant controller to do the rest of the work?
My approach eventually was to extend the _parse_routes() method of the Router class to check the database for any records matching the current uri and set the request with the corresponding value from the database.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Router extends CI_Router {
function __construct() {
parent::__construct();
}
function _parse_routes() {
require_once( BASEPATH .'database/DB'. EXT );
$db =& DB();
$routes_table_exists = $db->query("SHOW TABLES LIKE 'routes';");
if ($routes_table_exists->num_rows > 0) {
$uri_routes = $db->get_where('routes', array('uri' => $this->uri->uri_string()));
if ($uri_routes->num_rows > 0) {
$row = $uri_routes->result()[0];
if (isset($row->request)) {
return $this->_set_request(explode('/', $row->request));
}
}
}
parent::_parse_routes();
}
}
Whether or not it's the best approach to take it seems to work so far.
Usually its a combination of Routes and naming your controllers. so for example you have an About page, and you don't need a separate About controller. Lets say you have a general Pages controller, and then a view($page) method to retrieve and show the page.
example.com/about
$route['about'] = "pages/view/about";
if you just have a few pages there are advantages to hard coding the routes - it protects your database. but otherwise taking an example from the tutorial
$route['default_controller'] = 'pages/view';
$route['(:any)'] = 'pages/view/$1';
this does the same thing but now it will take example.com/anything can go here
Versus something like a contact page - where you probably want to have a separate controller called Contact, because you will need to validate the contact form, add it to a database, email it, show a response, show the form again if did not validate, etc So then you can just do a simple link to show the contact form: example.com/contact
the contact form submits to: example.com/contact/submit
more about Routes
http://ellislab.com/codeigniter/user-guide/general/routing.html
and definitely look at the tutorial it will give you more examples about routes
http://ellislab.com/codeigniter/user-guide/tutorial/index.html
Let's pretend I'm trying to learn CI, and as my test project I am building a group-buying site.
What I'd like is to have a different page for each city, e.g.:
http://www.groupon.com/las-vegas/
http://www.groupon.com/orlando/
I'd also like to have different pages such as:
http://www.groupon.com/learn
http://www.groupon.com/contact-us
If I am building this in CI and following the MVC ideology, how would this work? I'm having difficulty seeing how to accomplish the desired URL's with the concept of:
http://www.domain.com/controller/view/segment_a/segment_b/etc...
What I would do is create a custom 404 controller that acts as a catch-all for non-existent routes.
It would take the URI, possibly validate it, and re-route it to the (e.g.) "city" controller.
If the city controller can't find the city (whatever string was specified), then it needs to issue a proper 404. Otherwise, you're good to display your information for that city.
Also, once you create your custom 404 controller, you can send all 404 errors to it by specifying a route named '404_override'.
That's where URI Routing comes in. But in your case you'll probably will have to be carefull defining your routes as the first and only part of your route is a variable part already.
This really has nothing to do with MVC, and much more to do with good URL.
You're looking for URLs that are both (a) clear from the user's point of view and (b) that give hints to your application as to how it's meant to be handled.
What I'd do in this case is redesign your URLs slightly so that rather than:
http://www.groupon.com/las-vegas/
http://www.groupon.com/orlando/
You would have URLs that looks like this:
http://www.groupon.com/destinations/las-vegas/
http://www.groupon.com/destinations/orlando/
The bit at the beginning--/destinations/--can be used by your URL routing code to decide what controller should be dealing with it. If your routing code is URL-based, you might have an array like this:
$routes = array(
'/destinations/' => 'on_destination_list',
'/destinations/(.+)' => 'on_destination',
'/(.*)' => 'on_page');
// Basic URI routing code based off of REQUEST_URI
foreach ($pattern => $func) {
if (preg_match("`^$pattern$`", $_SERVER['REQUEST_URI'], $placeholders)) {
array_shift($placeholders);
call_user_func($func, $placeholders);
}
}
Keep in mind that I wrote that routing code off the top of my head and it may not be absolutely correct. It should give you the gist of what you need to do.
Doing things this way has the added benefit that if somebody goes to http://www.groupon.com/destinations/, you'll have the opportunity to show a list of destinations.
while i'm well awared this topic might have come a number of time, i still think that the perspective from which i'm looking at it is different.
I have a ZF 1.10.8 project whith essentially ContentController to manage what i call static pages (not so static anyway) like about us, contact us, products and NewsController for articles, tutorials, all writeups.
i've found that having a dynamic menu will solve a lot of complains from the client and gives more freedom changing the content of the site.
currently i only a main menu bar which is a partial (under layouts/scripts/partials/_mainmenu.phtml folder) which i call in every layout that exists in the system.
Now if i go dynamic, and a new link is created let's say category, how to map the category page to a route (routes are in /application/configs/routes.ini) since the url would be the value of the link in the menu table in the database?
the first thought is to change everything to resource handled by the NewsController to even about us will be an article in that case.Since those that i referenced as static pages require different view i wouldn't know how to handle them.
I'm kind of uncomfortable with my way of thinking it.
Can anyone point me to the right direction please? How would you do if? how joomla guys do it?
thanks for reading.....
i am using named routes to build links in menu
new Zend_Navigation_Page_Mvc(array(
'label' => $category->getTitle(),
'route' => 'catalog-category',
'params' => array('id' => $category->getId()),
));
exact module/controller/action mapping handled by routes
module specific routes defined in module bootstrap.
i cant give you more on this as my current routes implementation are too tricky and fragile. must heavily refactor it first.
My approach is to have a plugin that in routeStartup() checks for the existence of the current URI in a database, if the URI is found then the correct route is added using this function:
protected function _createRoute($route, $name, $action = "index", $controller = "index", $module = "default", $params)
{
$router = Zend_Controller_Front::getInstance()->getRouter();
$defaults = array('controller' => $controller,
'action' => $action,
"module" => $module);
$route = new Zend_Controller_Router_Route($route, array_merge($defaults, $params));
$router->addRoute("region", $route);
}
I don't have a limitless number of controllers and modules so the parameters for this function are hard coded into the plugin, but they could easily for stored against the row in the DB to be more dynamic.
Adding the plugin do this means that after routeStartup() is complete the new correct route is available for the dispatch operation.
I'm pretty new to code igniter.
Is there best practice for serving different view for different context.
For example, I'd like to serve specific pages for mobile user agents with the same controllers.
There isn't a hard rule for this. You can structure your view files however you like, and call $this->load->view() to load different view files for different outcomes in your controller. From my experience, CodeIgniter adapts very openly to how you organize your application's files.
In your example, perhaps I'd divide my system/application/views folder into two subfolders: main for desktop browsers, and mobile for mobile browsers:
system/
application/
views/
main/
index.php
some_page.php
...
mobile/
index.php
some_page.php
...
In an early part of your controller, say the constructor, you can decide what user agent is requesting it and then pick main or mobile based on that, then show your views accordingly from your controller actions.
Some quick code snippets to give you a better idea since you're new...
// Place this just below the controller class definition
var $view_type = 'main';
// Controller constructor
function MyController()
{
parent::Controller();
if ($this->agent->is_mobile())
{
$this->view_type = 'mobile';
}
else
{
$this->view_type = 'main';
}
}
// Example action
function some_page()
{
// ...
// This comes from the 'var $view_type;' line above
$this->load->view($this->view_type . '/some_page');
}
And some helpful references for you to explore:
Views in CodeIgniter
User Agent Class
Hope my explanation helps, and hope you have fun with CodeIgniter :)
I've just put together a very basic site using the Zend Framework and its MVC. (Actually, I'm not even using models at the moment, it's all just Controllers/Views for static info so far).
When I started toying with the Forms I realized that in the examples for Zend_Form they use something like this this set the form's action:
$form->setAction('/user/login')
Which contains the URL. I understand that Zend Framework has Routes and that they can be named, but I can't seem to grasp from the manual how to create a simple route for certain Controller/Actions so that I could do something like this:
$form->setAction($named_route)
// or
$form->setAction('named_route')
I hope my question is clear. I wasn't able to locate any duplicate questions, but if you spot one and point it out I won't mind.
Links to resources are as good as examples, so don't waste your time if you know of a decent blog post somewhere. Thanks!
References:
http://framework.zend.com/manual/en/zend.controller.router.html#zend.controller.router.routes.standard - Look for "12.5.7.1. Zend_Controller_Router_Route" for a clearer explanation.
This is not the best way to do it, but I have a working example. I welcome corrections.
After seeing Shorten Zend Framework Route Definitions, I agree that named Routes should go in their own config (I use Django, and named Views/URLs are generally separated) - but here I'm just going to define Routes in my Bootstrap.
So, in Bootstrap.php, inside the Bootstrap Class of course, I've created an function that will be automatically run, like so:
public function _initRoutes()
{
$frontController = Zend_Controller_Front::getInstance();
$route = new Zend_Controller_Router_Route(
'login/', // The URL, after the baseUrl, with no params.
array(
'controller' => 'login', // The controller to point to.
'action' => 'index' // The action to point to, in said Controller.
)
);
$frontController->getRouter()->addRoute('loginpage', $route);
}
In the above example, "loginpage" will be the Name of the "Named Route".
So, inside my LoginController, (in a function that builds the form) instead of doing
$form->setAction('/blah/login')
I retrieve the URL of the named Route and pass that in, like so:
$form_action_url = $this->view->Url(array(), 'loginpage', true);
// -- SNIP --
$form->setAction($form_action_url) // ...
This may be pointless and wrong, but it seems to work at the moment.
My reason for wanting a named URL, when Zend Framework handles URLs as /Controller/View(Action)/ automatically is because I'm anal about that kind of thing. I've been using Django for awhile, where the URLs are predefined, and I like it that way.
The Zend Framework MVC urls working out of the box is nice, tho.
Feel free to add notes and corrections to how this should work!