SitePoint Sponsor

User Tag List

Results 1 to 16 of 16

Hybrid View

  1. #1
    SitePoint Enthusiast
    Join Date
    Jun 2011
    Location
    Singapore
    Posts
    29
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question MVC Application - Need some guidance

    Hi all, I am building an Ajax driven PHP application that uses a single page and want to implement the MVC pattern. Every time I think I have my head around the concepts I try to implement them and hit another wall. I was hoping to use this thread to post the issues I come across as I encounter than and hopefully work through the problems I'm having.

    Question 1: Is the model in MVC a domain object?

    Let's say for instance I have a library application and need to handle searching for, getting details of, adding and deleting books. Should my model be the book object, or should it use the book objects?

    Examples:

    1. Without Book domain object.

    PHP Code:
    class BookModel extends BaseModel
    {


        public function 
    getAll()
        {
            
    $sql    'SELECT * FROM Books';
            
    $stmt   $this->database->query($sql);
            
    $values = array();

            while (
    $row $stmt->fetch()) {
                
    array_push($values$row);
            }

            return 
    $values;

        }
    //end getAll()


        
    public function getBookByName($name)
        {
            
    $sql  'SELECT * FROM Books WHERE bookName = '.$name;
            
    $stmt $this->database->query($sql);

            
    $row $stmt->fetch();
            return 
    $row;

        }
    //end getBookByName()


    }//end class 
    2. With Book domain object.

    PHP Code:
    class BookModel extends BaseModel
    {


        require 
    '../app/classes/Book.php';

        public function 
    getAll()
        {
            
    $sql    'SELECT * FROM Books';
            
    $stmt   $this->database->query($sql);
            
    $values = array();

            while (
    $row $stmt->fetch()) {
                
    $book = new Book($row['id'], $row['title'], $row['isdn'], $row['author']);
                
    array_push($values$book);
            }

            return 
    $values;

        }
    //end getAll()


        
    public function getBookByName($name)
        {
            
    $sql  'SELECT * FROM Books WHERE bookName = '.$name;
            
    $stmt $this->database->query($sql);

            
    $row $stmt->fetch();
            
    $book = new Book($row['id'], $row['title'], $row['isdn'], $row['author']);
            return 
    $book;

        }
    //end getBookByName()


    }//end class 
    I am assuming if I am using domain objects I would want to be even smarter than this?

    PHP Code:
    class BookModel extends BaseModel
    {


        require 
    '../app/classes/Book.php';

        public function 
    getBookByName($name)
        {
            
    $book = new Book();
            
    $book->find($name);
            return 
    $book;

        }
    //end getBookByName()


    }//end class 
    The above seems to make more sense, but how would you implement the findAll() function then? It can't be the method of a single book, do I need an object for Book and an object for BookCollection?

    PHP Code:
    class BookModel extends BaseModel
    {


        require 
    '../app/classes/Book.php';
        require 
    '../app/classes/BookCollection.php';

        public function 
    getAll()
        {
            
    $bookCollection = new BookCollection();
            
    $bookCollection->getAll();
            return 
    $bookCollection();

        }
    //end getAll()


    }//end class 

    Question 2: Should database access exist in the model?

    It doesn't feel write to me, but how do you implement database access otherwise? Does the below make more sense?

    Code:
    Controller  <->  Model  <->  Domain Object  <->  Database Access
                <->  View
    If so; how do you structure you Models, Domain Objects and Database Access components to talk to each other?

    I have more questions but I'd like to get my head clear around these two first. Then I can take this further.

    Appreciate any advice.

  2. #2
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,433
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Hi Jordan,

    In answer to your first question, the Model in MVC is a layer rather than a specific object or type of object. Domain objects form part of your model layer, as does persistence (this article gives a nice breakdown of the MVC pattern).

    As for your second question, Active Record based ORMs encourage you to combine domain logic with persistence, but this is widely considered to be bad practice as it violates the Single Responsibility Principle (SRP). Have a look at the data mapper pattern as a better solution.

  3. #3
    SitePoint Enthusiast
    Join Date
    Jun 2011
    Location
    Singapore
    Posts
    29
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks @fretburner ;, this clears things up a little. If I understand correctly the M in MVC = Domain Objects + Data Acess Layer.

    Would this be how you implement?

    PHP Code:
    class BookModel extends BaseModel 



        require 
    'BookMapper.php';

        private 
    $__bookMapper;


        public function 
    __construct()
        {
            
    $this->__bookMapper = new BookMapper();

        }
    //end __construct()


        
    public function getBooks() 
        { 
            
    $books $this->__bookMapper->fetchAll(); 
            return 
    $books

        }
    //end getBooks() 


        
    public function getBookByTitle($title
        { 
            
    $book $this->__bookMapper->fetchByTitle($title); 
            return 
    $book

        }
    //end getBookByTitle() 


    }//end class  


    class Book 


        public 
    $id;
        public 
    $title;
        public 
    $isbn;
        public 
    $author;

        public function 
    __construct($id$title$isbn$author)
        {
            
    $this->id     $id;
            
    $this->title  $title;
            
    $this->isbn   $isbn;
            
    $this->author $author;

        }
    //end __construct()

       
    }//end class 


    class BookMapper
    {

        require 
    'Book.php'

        protected 
    $database;

        public function 
    __construct()
        {
            
    $appconfig parse_ini_file('../app/config/myProject.cfg');
            
    $config    = new Doctrine\DBAL\Configuration();

            
    $connectionParams = array(
                                 
    'dbname'   => $appconfig['dbname'],
                                 
    'user'     => $appconfig['user'],
                                 
    'password' => $appconfig['password'],
                                 
    'host'     => $appconfig['host'],
                                 
    'driver'   => $appconfig['driver'],
                                );

            
    $this->database = \Doctrine\DBAL\DriverManager::getConnection($connectionParams$config);

        }
    //end __construct()


        
    public function fetchAll()
        {

            
    $sql    'SELECT * FROM books';
            
    $stmt   $this->database->query($sql);
            
    $books = array();

            while (
    $row $stmt->fetch()) {
                
    $book = new Book($row['id'], $row['title'], $row['isbn'], $row['author']);
                
    array_push($books$book);
            }

            return 
    $books;

        }
    //end fetchAll()


        
    public function fetchByTitle($title)
        {
            
    $sql  'SELECT * FROM books WHERE bookTitle = '.$title;
            
    $stmt $this->database->query($sql);

            
    $row $stmt->fetch();
            
    $book = new Book($row['id'], $row['title'], $row['isbn'], $row['author']);

            return 
    $book;

        }
    //end fetchByTitle()


    }//end class 
    And my controller:
    PHP Code:
    class Books extends BaseController
    {


        protected function 
    getAll()
        {
            
    $viewmodel = new BookModel();
            
    $this->ReturnView($viewmodel->getBooks(), false);

        }
    //end getAll()


        
    protected function getBook()
        {
            
    $viewmodel = new BookModel();
            
    $this->ReturnView($viewmodel->getBookById($this->urlvalues['title']), false);

        }
    //end getPromotion()


    }//end class


    abstract class BaseController
    {

        protected 
    $urlvalues;

        protected 
    $action;


        public function 
    __construct($action$urlvalues)
        {
            
    $this->action    $action;
            
    $this->urlvalues $urlvalues;

        }
    //end __construct()


        
    public function executeAction()
        {
            return 
    $this->{$this->action}();

        }
    //end executeAction()


        
    protected function returnView($viewmodel$fullview)
        {
            
    $viewloc '../app/views/'.get_class($this).'/'.$this->action.'.php';
            if (
    $fullview === true) {
                include 
    '../app/views/maintemplate.php';
            } else {
                include 
    $viewloc;
            }

        }
    //end returnView()


    }//end class 
    Last edited by Jordan Windebank; Dec 29, 2013 at 20:46. Reason: Adding controllers.

  4. #4
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,433
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Hi Jordan,

    You don't need the BookModel class.. you can just inject BookMapper into your controller and call fetchAll / fetchByTitle directly.

    A couple of points about your mapper class: First, you don't really want to be including files within your classes, use autoloading instead. Second, the contructor knows way too much about the database connection.. build the connection object first and then pass it into the mapper.

  5. #5
    SitePoint Enthusiast
    Join Date
    Jun 2011
    Location
    Singapore
    Posts
    29
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks, I have taken this a bit further now including autoloading. I have also set up a base DataMapper class that the domain specific mappers extend, and this has the database connection. If I was going to build it first, where would it belong? Below is what I have so far.

    index.php
    PHP Code:
    <?php

    use FightSportsDB\App\Common;

    // Set up Autoloaders
    require_once '../App/Common/SplClassLoader.php';
    $classLoader = new Common\SplClassLoader('FightSportsDB''.\..\..');
    $classLoader->register();

    $doctrineClassLoader = new Common\SplClassLoader('Doctrine''../App/lib');
    $doctrineClassLoader->register();

    // Create the controller and execute the action.
    $loader     = new Common\Loader($_GET);
    $controller $loader->CreateController();
    $controller->ExecuteAction();
    FightSportsDB\App\Common\Loader
    PHP Code:
    <?php

    namespace FightSportsDB\App\Common;

    use 
    FightSportsDB\App\Controller;

    /**
     * FightSportsDB Loader Doc Comment
     *
     * PHP version 5
     *
     * @category Application
     * @package  FightSportsDB
     * @author   Jordan Windebank
     * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
     * @link     http://fightsportsdb.com
     *
     */

    /**
     * Class Loader
     *
     * PHP version 5
     *
     * @category Application
     * @package  FightSportsDB
     * @author   Jordan Windebank
     * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
     * @link     http://fightsportsdb.com
     */
    class Loader
    {

        private 
    $_controller;

        private 
    $_action;

        private 
    $_urlvalues;

        
    /**
         * Store the URL values on object creation
         *
         * @param array $urlvalues Input parameters
         *
         * @return mixed
         */

        
    public function __construct($urlvalues)
        {
            
    $this->_urlvalues $urlvalues;
            if (
    $this->_urlvalues['controller'] === '' || isset($this->_urlvalues['controller']) === false) {
                
    $this->_controller 'Home';
            } else {
                
    $this->_controller 'FightSportsDB\\App\\Controller\\'.$this->_urlvalues['controller'].'Controller';
            }

            if (
    $this->_urlvalues['action'] === '' || isset($this->_urlvalues['action']) === false) {
                
    $this->_action 'index';
            } else {
                
    $this->_action $this->_urlvalues['action'];
            }

        }
    //end __construct()


        /**
         * Establish the requested controller as an object
         *
         * @return mixed
         */

        
    public function createController()
        {
            
    // Does the class exist?
            
    if (class_exists($this->_controller) === true) {
                
    $parents class_parents($this->_controller);
                
    // Does the class extend the controller class?
                
    if (in_array('FightSportsDB\\App\\Controller\\BaseController'$parents) === true) {
                    
    // Does the class contain the requested method?
                    
    if (method_exists($this->_controller$this->_action) === true) {
                        return new 
    $this->_controller($this->_action$this->_urlvalues);
                    } else {
                        
    // Bad method error.
                        
    return new Controller\ErrorController('invalidAction'$this->_urlvalues);
                    }
                } else {
                    
    // Bad controller error.
                    
    return new Controller\ErrorController('invalidController'$this->_urlvalues);
                }
            } else {
                
    // Bad controller error.
                
    return new Controller\ErrorController('invalidController'$this->_urlvalues);
            }

        }
    //end createController()


    }//end class
    FightSportsDB\App\Controller\BaseController
    PHP Code:
    <?php

    namespace FightSportsDB\App\Controller;

    abstract class 
    BaseController
    {

        protected 
    $urlvalues;

        protected 
    $action;


        public function 
    __construct($action$urlvalues)
        {
            
    $this->action    $action;
            
    $this->urlvalues $urlvalues;

        }
    //end __construct()


        
    public function executeAction()
        {
            return 
    $this->{$this->action}();

        }
    //end executeAction()


        
    protected function returnView($viewmodel$fullview)
        {
            
    $className get_class($this);
            
    $lastNsPos strripos($className'\\');
            
    $className substr($className, ($lastNsPos 1));
            
    $className substr($className0, -10);
            
    $viewloc '../App/View/'.$className.'/'.$this->action.'.php';
            if (
    $fullview === true) {
                include 
    '../App/View/maintemplate.php';
            } else {
                include 
    $viewloc;
            }

        }
    //end returnView()


    }//end class
    FightSportsDB\App\Controller\PromotionController
    PHP Code:
    <?php

    namespace FightSportsDB\App\Controller;

    use 
    FightSportsDB\App\Model;

    class 
    PromotionController extends BaseController
    {


        protected function 
    getAll()
        {
            
    $viewmodel = new Model\PromotionModel();
            
    $this->ReturnView($viewmodel->getPromotions(), false);

        }
    //end getAll()


        
    protected function getPromotion()
        {
            
    $viewmodel = new Model\PromotionModel();
            
    $this->ReturnView($viewmodel->getPromotionById($this->urlvalues['id']), false);

        }
    //end getPromotion()


    }//end class
    FightSportsDB\App\Model\BaseModel
    PHP Code:
    <?php

    namespace FightSportsDB\App\Model;

    abstract class 
    BaseModel
    {

        protected 
    $mapper;


        public function 
    __construct()
        {

        }
    //end __construct()


    }//end class
    FightSportsDB\App\Model\PromotionModel
    PHP Code:
    <?php

    namespace FightSportsDB\App\Model;

    use 
    FightSportsDB\App\DataMapper;

    class 
    PromotionModel extends BaseModel
    {


        public function 
    __construct()
        {
            
    $this->mapper = new DataMapper\PromotionMapper();

        }
    //end __construct()


        
    public function getPromotions()
        {
            
    $promotions $this->mapper->fetchAll();
            return 
    $promotions;

        }
    //end getPromotions()


        
    public function getPromotionByName($name)
        {
            
    $promotion $this->mapper->fetchByName($name);
            return 
    $promotion;

        }
    //end getPromotionByName()


        
    public function getPromotionById($id)
        {
            
    $promotion $this->mapper->fetchById($id);
            return 
    $promotion;

        }
    //end getPromotionById()


    }//end class
    FightSportsDB\App\Model\Domain\Promotion
    PHP Code:
    <?php

    namespace FightSportsDB\App\Model\Domain;

    /**
     * FightSportsDB Promotion Doc Comment
     *
     * PHP version 5
     *
     * @category Application
     * @package  FightSportsDB
     * @author   Jordan Windebank
     * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
     * @link     http://fightsportsdb.com
     *
     */

    /**
     * Class Promotion
     *
     * PHP version 5
     *
     * @category Application
     * @package  FightSportsDB
     * @author   Jordan Windebank
     * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
     * @link     http://fightsportsdb.com
     */
    class Promotion
    {

        
    /**
         * @var int
         */
        
    public $id;

        
    /**
         * @var string
         */
        
    public $name;

        
    /**
         * @var string
         */
        
    public $image;

        
    /**
         * @var string
         */
        
    public $imageType;


        
    /**
         * Store the Promotion values on object creation
         *
         * @param int    $promotionId   Promotion ID
         * @param string $promotionName Name of promotion
         * @param string $image         Address of promotion image
         * @param string $imageType     Type of promotion image [Logo|Fanart]
         */
        
    public function __construct($promotionId$promotionName$image$imageType)
        {
            
    $this->id        $promotionId;
            
    $this->name      $promotionName;
            
    $this->image     '../FightSportsDB/Public/images/'.$image;
            
    $this->imageType $imageType;

        }
    //end __construct()


    }//end class
    FightSportsDB\App\Model\DataMapper
    PHP Code:
    <?php

    namespace FightSportsDB\App\DataMapper;

    use 
    Doctrine\DBAL;

    abstract class 
    BaseMapper
    {

        protected 
    $database;

        public function 
    __construct()
        {
            
    $appconfig parse_ini_file('../App/Config/fightsportsdb.cfg');
            
    $config    = new DBAL\Configuration();

            
    $connectionParams = array(
                                 
    'dbname'   => $appconfig['dbname'],
                                 
    'user'     => $appconfig['user'],
                                 
    'password' => $appconfig['password'],
                                 
    'host'     => $appconfig['host'],
                                 
    'driver'   => $appconfig['driver'],
                                );

            
    $this->database DBAL\DriverManager::getConnection($connectionParams$config);

        }
    //end __construct()


    }//end class
    FightSportsDB\App\Model\PromotionMapper
    PHP Code:
    <?php

    namespace FightSportsDB\App\DataMapper;

    use 
    FightSportsDB\App\Model\Domain;

    class 
    PromotionMapper extends BaseMapper
    {


        public function 
    fetchAll()
        {
            
    $sql        "SELECT
                             p.promotionId AS 'id',
                             p.promotionName AS 'name',
                             a.artworkPath As 'image',
                             aType.artworkType As 'imageType'
                           FROM
                                Promotions As p
                           INNER JOIN PromotionsArtwork As pa ON p.promotionId = pa.promotionId
                           INNER JOIN Artwork As a ON pa.artworkId = a.artworkId
                           INNER JOIN ArtworkTypes As aType ON pa.artworkTypeId = aType.artworkTypeId"
    ;
            
    $stmt       $this->database->query($sql);
            
    $promotions = array();

            while (
    $row $stmt->fetch()) {
                
    $promotion = new Domain\Promotion($row['id'], $row['name'], $row['image'], $row['imageType']);
                
    array_push($promotions$promotion);
            }

            return 
    $promotions;

        }
    //end fetchAll()


        
    public function fetchByName($name)
        {
            
    $sql       'SELECT * FROM Promotions WHERE promotionName = '.$name;
            
    $stmt      $this->database->query($sql);
            
    $row       $stmt->fetch();
            
    $promotion = new Domain\Promotion($row['id'], $row['name'], $row['image'], $row['imageType']);

            return 
    $promotion;

        }
    //end fetchByName()


        
    public function fetchById($id)
        {
            
    $sql       'SELECT * FROM Promotions WHERE promotionId = '.$id;
            
    $stmt      $this->database->query($sql);
            
    $row       $stmt->fetch();
            
    $promotion = new Domain\Promotion($row['id'], $row['name'], $row['image'], $row['imageType']);

            return 
    $promotion;

        }
    //end fetchById()


    }//end class
    Now this is working as I expected it to (though some sections are clearly unfinished), and seems as though it would be fairly easy to build out the rest of my controllers/models in the same way. Is there anything I am doing fundamentally wrong?

    My next major stumbling block is my view.

    Question 3: How do I build out my view capability to allow me to load multiple sections of data on a single page?

    I will end up with individual controllers/models to handle promotions, events, matches & competitors and would want to bring them all in one the main landing page. I have looked at the Composite View pattern but am having trouble finding any material online that is easy to understand how to implement. Should I set up a HomeController that gets data from multiple models (the model) and have a single view to inject it all in to?

    Thanks again, this has been an excellent learning exercise so far.

  6. #6
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,433
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    If you're looking to get suggestions on your code, it might be worth posting a link to your project over at SitePoints new code review forum.

    Your Loader object is perhaps a little confusingly named, as it's actually a factory rather than a loader. I think you might find this example project interesting - it does something similar, with an application factory that creates controllers and mappers etc.

  7. #7
    SitePoint Guru
    Join Date
    Nov 2003
    Location
    Huntsville AL
    Posts
    692
    Mentioned
    4 Post(s)
    Tagged
    1 Thread(s)
    With respect to question 3, your first post indicates that this will be an ajax driven application. So each section of data will generate their own ajax call and get just the information it needs. This will simplify the server side code because you will never need to deal with multiple unrelated data sets.

    So the answer to Q3 is: You don't.

  8. #8
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,433
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Quote Originally Posted by ahundiak View Post
    With respect to question 3, your first post indicates that this will be an ajax driven application. So each section of data will generate their own ajax call and get just the information it needs. This will simplify the server side code because you will never need to deal with multiple unrelated data sets.
    And what about user's who have JS disabled?

  9. #9
    SitePoint Guru
    Join Date
    Nov 2003
    Location
    Huntsville AL
    Posts
    692
    Mentioned
    4 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by fretburner View Post
    And what about user's who have JS disabled?
    Users with javascript disabled would not be able to use an ajax driven application.

  10. #10
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,433
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Quote Originally Posted by ahundiak View Post
    Users with javascript disabled would not be able to use an ajax driven application.
    there's often very little reason other than developer laziness for building a site that doesn't work with JS disabled. If you stick to the principle of progressive enhancement you make your site accessible to all users.

  11. #11
    SitePoint Guru
    Join Date
    Nov 2003
    Location
    Huntsville AL
    Posts
    692
    Mentioned
    4 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by fretburner View Post
    there's often very little reason other than developer laziness for building a site that doesn't work with JS disabled. If you stick to the principle of progressive enhancement you make your site accessible to all users.
    I freely admit to being lazy. Why work harder than you have to?

    Progressive enhancement works fine for doing things like adding a pop up calendar to an input element. No js mean no popup but the user can still enter a value. Best of all, you can achieve progressive enhancement without any additional effort on the part of the developer.

    But adding ajax to the mix changes things completely. Want to load option values for a select element dynamically? Easy with ajax. But wait. No js. How the heck am I supposed to get the options now? Maybe I'll always send the options or maybe I'll add some sort of load options button or maybe do some kind of funky js detection. Way to much work.

    Us lazy developers would simple write two front ends. One for js and one (if needed) without js.

    This is particular true now that full fledged js application frameworks (such as AngularJS) have now reached critical mass. Progressive enhancements for these frameworks is not even an option.

  12. #12
    SitePoint Addict bronze trophy Hall of Famer's Avatar
    Join Date
    Apr 2013
    Location
    Ithaca
    Posts
    368
    Mentioned
    6 Post(s)
    Tagged
    2 Thread(s)
    Looks like you are making some great progress, I definitely love to see more users here asking questions about OOP, MVC, ORM or architecture design since its good for me to learn and experience too. One thing I notice is that your model is somewhat different from a standard domain model, and you have used the phrase 'view_model' for it. Are you sure your approach is not MVVM? In a typical MVC application its rare to see models creating and using mapper objects, and in fact a traditional MVC model should not even be aware of the existence of a mapper(since technically, they belong to different layers of your application). I am curious at whether you have an explanation for what you are doing, its kinda interesting I must say. Or are you following some kind of guideline from an advanced PHP tutorial?

    On the other hand, I have one suggestion for your loader class' createController() method. You have a set of duplicate code that creates an Error Controller, they trigger when a series of if conditions returns false. Duplicate code like this makes your program less organized and less maintainable, one solution to the problem is to create a helper method in your loader class that checks these if conditions and return a boolean true or false value. This way the createController() only needs to run the helper method and checks if it returns true or false, which creates the appropriate controller object or an error controller, heres how it may look like, you may also use try/catch exception here since an invalid controller/action is clearly an error that needs to be handled somehow.

    PHP Code:
    public function createController(){
        return (
    $this->validateController())?$this->_controller($this->_action$this->_urlvalues):new Controller\ErrorController('invalidAction'$this->_urlvalues); 
    }

    public function 
    validateController(){
        if(
    class_exists($this->_controller)){
            
    $parents class_parents($this->_controller); 
            if(
    in_array('FightSportsDB\\App\\Controller\\BaseController'$parents) and method_exists($this->_controller$this->_action))  return TRUE;       
        } 
        return 
    FALSE;

    At last but not least, I personally disagree with the fact that a site has to check for non-javascript users. In modern web era javascript is pretty much enabled on every client except for the very few who refuse to move on or are way too cautious on potential security issues. I dont think its necessary to compensate for these users, just like there is no need for a modern PHP software to worry about PHP 4 compatibility. Correct me if I was wrong, but I dont consider the number of non-javascript users are so vast that one needs to invest a huge amount of time, energy and possibly money on handling them properly.

  13. #13
    SitePoint Enthusiast
    Join Date
    Jun 2011
    Location
    Singapore
    Posts
    29
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sorry for the late reply, plenty to absorb here.

    Quote Originally Posted by Hall of Famer View Post
    Looks like you are making some great progress, I definitely love to see more users here asking questions about OOP, MVC, ORM or architecture design since its good for me to learn and experience too. One thing I notice is that your model is somewhat different from a standard domain model, and you have used the phrase 'view_model' for it. Are you sure your approach is not MVVM? In a typical MVC application its rare to see models creating and using mapper objects, and in fact a traditional MVC model should not even be aware of the existence of a mapper(since technically, they belong to different layers of your application). I am curious at whether you have an explanation for what you are doing, its kinda interesting I must say. Or are you following some kind of guideline from an advanced PHP tutorial?
    To be honest I've just been fudging this together with bits and pieces I'm finding in articles, tutorials and sample code and then trying to keep it clean. I don't yet understand the difference between MVC and MVVM but I have controllers so does that make it MVVMC?

    Quote Originally Posted by Hall of Famer View Post
    On the other hand, I have one suggestion for your loader class' createController() method. You have a set of duplicate code that creates an Error Controller, they trigger when a series of if conditions returns false. Duplicate code like this makes your program less organized and less maintainable, one solution to the problem is to create a helper method in your loader class that checks these if conditions and return a boolean true or false value. This way the createController() only needs to run the helper method and checks if it returns true or false, which creates the appropriate controller object or an error controller, heres how it may look like, you may also use try/catch exception here since an invalid controller/action is clearly an error that needs to be handled somehow.

    PHP Code:
    public function createController(){
        return (
    $this->validateController())?$this->_controller($this->_action$this->_urlvalues):new Controller\ErrorController('invalidAction'$this->_urlvalues); 
    }

    public function 
    validateController(){
        if(
    class_exists($this->_controller)){
            
    $parents class_parents($this->_controller); 
            if(
    in_array('FightSportsDB\\App\\Controller\\BaseController'$parents) and method_exists($this->_controller$this->_action))  return TRUE;       
        } 
        return 
    FALSE;

    Thanks for the suggestions. I am going to rethink this project based on ahundiak's comment above but will definitely keep this in mind.

    Quote Originally Posted by ahundiak View Post
    There are significant differences between ajax applications and non-ajax applications. For the non-ajax application, the server has the responsibility to put together a complete html page for each request. Lots of state. Lots of template processing, etc. With ajax, much of the application can be shifted to the client. The server only needs to serve up or process bits of json on request. So it's probably not a bad idea to focus on the ajax side first since using ajax can really change your entire application design.

    As you say, implementing individual ajax requests is easy enough. The challenge comes in wiring together all the little bits and pieces necessary to make a smooth application. And this is where the newer javascript frameworks (as opposed to libraries like jquery) come in. You might consider spending some time with AngularJs 1.2 (http://angularjs.org/). The web site has a number of introductory videos as well as a fairly decent tutorial show how a simple ajax application can work. The only bad thing about the tutorial is that the very first page is a bit confusing and talks about installing Node.js and karma for running the unit tests. You don't need any of that to get started. Just pull the code from github and point your browser to index.html.

    Also some nice basic videos at: https://egghead.io/lessons/angularjs-binding

    The point is that once you start to understand ajax based applications, many of your server side questions tend to go away. The server really only needs to handle bits of json for the most part. The page loading issues and what not become moot.
    Thank you! I've spent the last few days trying to get my head around AngularJS and it looks perfect for what I'm wanting to achieve. I'm going to look at redesigning the server side part of my application to focus purely on the API and start again from the front back in terms of UI and how to make that work with AngularJS.

    Next step is to plan and build out the API and will do this using a MVC pattern, which I think I'm clear on how to implement. Will post back in a few days with an update as to how I go.

    I really appreciate the ongoing feedback.


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •