SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 68

Thread: MVC vs PAC

  1. #1
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)

    MVC vs PAC

    Quote Originally Posted by Jeff Mott View Post
    I think most of your questions can be answered with this explanation:

    The definitions of MVC were written with desktop applications in mind, and it doesn't translate perfectly to stateless, server-side web applications. MVC controllers are supposed to handle user input -- which could be mouse clicks, key presses, touches, gestures, device orientation, etc -- and translate that input into commands for the view and the model. But in server-side web applications, there's only one input: the HTTP request. And MVC views are supposed to be able to react to user input, as well as to changes in the model. But in server-side web applications, there won't be any new user input for the view to react to, nor will there be any changes to the model that would require the view to update itself.

    Because of these differences, MVC was a bit bastαrdized in its adaptation to the web. Views in web applications are little more than templates, and the templates don't access the model directly. Instead they are fed data by the controller, which acts as a mediator between the model and the view. It turns out there's another design pattern that more accurately reflects the way server-side web applications work, called Presentation–abstraction–control.

    With that explanation out of the way, I'll add my two cents... that even though what most developers and frameworks have been using all this time isn't MVC in its purest sense, I think the architecture that web applications have settled on is a good one. For example, I think it's a good thing that the views/templates don't access the model directly. It lets them stay small in scope, ignorant of -- and independent of -- the larger application.

    at the expense of flexibility and the addition of repeated binding logic. The *******ised approach tightly couples the controller to the template and in some cases, the model, essentially locking everything together and making it inflexible. Keep in mind, it's the misunderstanding of the MVC model in the first place that caused this mess ( http://blog.astrumfutura.com/2008/12...unappreciated/ ) In any non-trivial application (and if you're using MVC your application will fall into "non-trivial") MVC models are not domain models. They're designed to exist as a bridge between the domain and the GUI. As such, it makes perfect sense for Views to know of them.

    As I've said before, the reason that we call PAC "MVC" in the web world is that people were trying to implement MVC. The architecture was stumbled upon by people accidentally missing the point of MVC. The original MVC architecture was created by academics who thought very carefully about what they were trying to achieve and it offers many advantages over the pseudo-MVC most frameworks offer. And the web actually lets you create a purer implementation of the architecture because you don't have to worry about refreshing views to account for state changes in the model.

  2. #2
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    at the expense of flexibility and the addition of repeated binding logic. The *******ised approach tightly couples the controller to the template and in some cases, the model, essentially locking everything together and making it inflexible. Keep in mind, it's the misunderstanding of the MVC model in the first place that caused this mess ( http://blog.astrumfutura.com/2008/12...unappreciated/ ) In any non-trivial application (and if you're using MVC your application will fall into "non-trivial") MVC models are not domain models. They're designed to exist as a bridge between the domain and the GUI. As such, it makes perfect sense for Views to know of them.

    As I've said before, the reason that we call PAC "MVC" in the web world is that people were trying to implement MVC. The architecture was stumbled upon by people accidentally missing the point of MVC. The original MVC architecture was created by academics who thought very carefully about what they were trying to achieve and it offers many advantages over the pseudo-MVC most frameworks offer. And the web actually lets you create a purer implementation of the architecture because you don't have to worry about refreshing views to account for state changes in the model.
    Genuine request here... perhaps you could start with the code from the "From Flat PHP to Symfony2" article I linked to above, just before the article adds "a Touch of Symfony2," and show additional step-by-step refactoring. Show a situation where you lose flexibility or repeat logic, and how you would fix that problem.
    "First make it work. Then make it better."

  3. #3
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Ok I'll use the first part of their example, a list of posts.

    Firstly, here's a domain model which provides very generic accessors to the domain state. I won't supply the full PDO backend but you can see what it's doing from the model API.

    PHP Code:
    class Posts {
        private 
    $pdo;
        
        public function 
    __construct(PDO $pdo) {
            
    $this->pdo $pdo;
        }

        public function 
    findByField($field$value$sortOrder) {
            
    //Pseudocode, ignore that this won't work...
            
    return $this->pdo->query("SELECT * FROM posts where $field = $value ORDER BY $sortOrder");
        }


        public function 
    findAll($sortOrder) {
            
    //Pseudocode, ignore that this won't work...
            
    return $this->pdo->query("SELECT * FROM posts ORDER BY $sortOrder");
        }

        public function 
    findById($id) {
            
    //Pseudocode, ignore that this won't work...
            
    return $this->pdo->query("SELECT * FROM posts where id = $id");
        }


    This is going to be reused throughout the application anywhere that posts are required.

    Next, here's a generic View which contains the logic that is common across any type of list:

    PHP Code:
    class ListView {
        private 
    $model;
        private 
    $template;

        public function 
    __construct(ListViewModel $modelTemplate $template) {
            
    $this->model $model;
        }

        public function 
    output() {
            
    $this->template->assign('items'$this->model->getRecords());
            return 
    $this->template->output();
        }

    And an interface to provide an API for the getRecords method:

    PHP Code:
    interface ListViewModel {
        public function 
    getRecords();

    Now we're ready to link the domain model to the GUI via a Model (MVC model, ViewModel, Application Model. Whatever terminology you want to use). The important distinction is that this holds application state rather than domain state. Since we're listing every single post, the application state is that "All posts have been selected". Not a great example. but I'll come to that later:

    PHP Code:
    class EveryPostList implements ListViewModel {
        private 
    $posts;

        public function 
    __construct(Posts $posts) {
            
    $this->posts $posts;
        }

        public function 
    getRecords() {
            return 
    $this->posts->findAll('date desc');
        }


    Since there is zero user interaction in a simple list there is no controller!


    Now we just wire everything up:

    PHP Code:
    $template = new Template('postList.tpl');
    $posts = new Posts($pdo);

    $model = new EveryPostList($posts);
    $view = new ListView($model$template);

    echo 
    $view->output(); 
    Advantage 1: There is no controller. Controller code is unnecessary by definition when there is no user interaction.

    Advantage 2: Because the controller isn't acting as an entry point and template selection is done by the router (or elsewhere within the front controller) it's easy to use a different template and I don't need to create another controller, I just wire everything up differently.

    Advantage 3: Everything is reusable. Let's say I only want posts by a specific user. I add a new Application model and just wire everything differently.


    PHP Code:
    class UserPostList implements ListViewModel {
        private 
    $posts;
        private 
    $userId;

        public function 
    __construct(Posts $posts$userId) {
            
    $this->posts $posts;
            
    $this->userId $userId;
        }

        public function 
    getRecords() {
            return 
    $this->posts->findByField('userId'$this->userId'date DESC');
        }

    I don't need to add a new controller just to achieve this, I just wire all the existing components differently:


    PHP Code:
    $template = new Template('postList.tpl');
    $posts = new Posts($pdo);

    $model = new UserPostList($posts123);
    $view = new ListView($model$template);

    echo 
    $view->output(); 

    Everything has been reused but I can wire this exact code with a different template, a different model, a view which paginates the list, etc.


    Anywhere I want a list of posts I can use this View and template combination along with the relevant application model that bridges the gap to the domain reducing repeated code dramatically.

    Advantage 4: Because the controller isn't doing View Selection, nesting views works with ease. Let's say I want to compare the posts of two users side by side. I already have the ability to get a list of all posts by a specific user, so that code can easily be reused:


    PHP Code:
    class NestedView {
        private 
    $views = array();
        private 
    $template;

        public function 
    __construct(Template $template) {
            
    $this->template $template;
        }

        public function 
    addView($nameView $view) {
            
    $this->views[$name] = $view;
        }

        public function 
    output() {
            
    $this->template->assign('children'$this->views);
            return 
    $this->template->output();
        }

    And it just gets wired up:

    PHP Code:
    $posts = new Posts($pdo);

    $view = new NestedView(new Template('compareTwoUsers.tpl');;
    $view->addView('user1', new ListView(new UserPostList($posts123), new Template('postList.tpl')));
    $view->addView('user2', new ListView(new UserPostList($posts321), new Template('postList.tpl')));


    echo 
    $view->output(); 
    The template would then do something like:

    PHP Code:
    <div style="width: 50%; float: left">
        <h2>First user's posts:</h2>
        <?php echo $this->children['user1']->output(); ?>
    </div>
    <div style="width: 50%; float: left">
        <h2>Second user's posts:</h2>
        <?php echo $this->children['user2']->output(); ?>
    </div>
    This is for demonstration, In reality I have a helper function in the template which builds the nested views. I basically supply a route such as $this->getView("/usersposts/123") and it uses the same router as the main application to build the MVC triad and return the view.


    Advantage 5: Controllers become agnostic of everything else.

    It's not unreasonable that in the example above I'd want to show the posts of a specific user rather than hardcoding it at the wiring stage! In fact, it's almost certain. This is information that's needed from the user and as such is where the controller is genuinely needed.


    Let's take our "Posts by a specific user" ViewModel from earlier, and amend it slightly:

    PHP Code:
    class UserPostList implements ListViewModel {
        private 
    $posts;
        private 
    $userId;

        public function 
    __construct(Posts $posts) {
            
    $this->posts $posts;
        }

        public function 
    setUser($id) {
            
    $this->userId $id;
        }

        public function 
    getRecords() {
            return 
    $this->posts->findByField('userId'$userId'date DESC');
        }

    I've made the userId mutable. It's the controllers job to set this state based on user input:


    PHP Code:
    class UserPostController {
        private 
    $userPostList;

        public function 
    __construct(UserPostList $userPostList) {
            
    $this->userPostList $userPostList;
        }

        public function 
    setUser($id) {
            
    $this->userPostList->setUser($id);
        }

    Imagine the router is routing /user/postlist/123 to $userPostController->setUser(123)

    Trivial stuff, but it's powerful. The viewModel doesn't care where/how it's being used. The view doesn't need a controller in order to exist and all the classes in the system really are this simplistic.

    Ok, that's simple. Let's go back to the nested view. I'll have a route such as /user/postcompare/123/321 to compare the posts of user 123 with user 312. I simply create a controller to allow for it:


    PHP Code:
    class UserPostCompareController {
        private 
    $userPostList1;
        private 
    $userPostList2;

        public function 
    __construct(UserPostList $userPostList1UserPostList $userPostList2) {
            
    $this->userPostList1 $userPostList1;
            
    $this->userPostList2 $userPostList2;
        }

        public function 
    setUsers($user1$user2) {
            
    $this->userPostList1->setUser($user1);
            
    $this->userPostList2->setUser($user2);
        }


    The two instances of UserPostList would be the same as the ones passed to the nested view:

    PHP Code:
    $posts = new Posts($pdo);

    $view = new NestedView(new Template('compareTwoUsers.tpl');;

    $postList1 = new ListView(new UserPostList($posts), new Template('postList.tpl'));
    $postList2 = new ListView(new UserPostList($posts), new Template('postList.tpl'));

    $view->addView('user1'$postList1);
    $view->addView('user2'$postList2);


    $controller = new UserPostCompareController($postList1$postList2);

    //The dispatcher would do this
    $controller->setUsers($_GET['arg1'], $_GET['arg2']); 

    echo 
    $view->output(); 

    And here, everything is reusable. Essentially any view can pull in any view with ease because Views don't have their logic tied up in controllers.


    Advantage 6: Actually this is more of a side-effect of better separation of concerns, but it's still an advantage: Controllers, views and models don't need to extend from weighty base classes. Controller, view and ViewModel code in my framework really is that sparse. You program the bare minimum around interfaces.



    Going back to the original user list, I can add a ViewModel and a Controller to deal with any set of criteria, e.g. searching the posts

    PHP Code:
    class SearchablePostsList implements ListViewModel {
        private 
    $posts;
        private 
    $criteria;

        public function 
    __construct(Posts $posts) {
            
    $this->posts $posts;    
        }
        
        public function 
    setCriteria($criteria) {
            
    $this->criteria $critera;
        }

        public function 
    getRecords() {
            
    //Query the model based on the criteria
            
    $return $this->posts->find('......');
        }

    }

    class 
    SearchablePostsController {
        private 
    $model;

        public function 
    __construct(SearchablePostsList  $model) {
            
    $this->model $model;
        }

        public function 
    main() {
            
    //Do something with posts and set:
            
    $this->model->setCriteria('...whatever!');

        }


    Any part of the system can be reused, I can reuse these viewmodels with different controller (perhaps one that uses GET and one that uses POST), I can reuse the controllers with different views. I can substitute the model entirely. For example. I might make a domain model which extends Posts and always filters out posts by a specific user unless you're logged in as an admin (All domain logic) and pass it in as $posts when I wire everything up.


    I'm afraid I don't have time to go any further this evening but feel free to ask any specific questions! Hopefully this will help shed some light on this approach.


    I'd like to see how you'd achieve similar in Symfony without ugly hacks such as invoking a controller when there hasn't been any user input, or ugly inheritance trees.

  4. #4
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Sorry, it's too late for me to edit but the wiring code for the nested view with the controller should look like this:


    PHP Code:
    $posts = new Posts($pdo);

    $view = new NestedView(new Template('compareTwoUsers.tpl');;

    $postList1 = new UserPostList($posts));
    $postList2 = new UserPostList($posts));

    $view->addView('user1',  new ListView($postList1, new Template('postList.tpl'));
    $view->addView('user2',  new ListView($postList2, new Template('postList.tpl'));


    $controller = new UserPostCompareController($postList1$postList2);

    //The dispatcher would do this
    $controller->setUsers($_GET['arg1'], $_GET['arg2']); 

    echo 
    $view->output(); 

  5. #5
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    I certainly appreciate all the effort you put in. When I asked for a step-by-step refactoring, I was kinda hoping to start at the first step of identifying the problem -- a concrete example where you lose flexibility or where you have to repeat logic. This looks like you went straight to the last step, your final solution, and skipped the intermediate refactoring steps.

    Quote Originally Posted by TomB View Post
    Since there is zero user interaction in a simple list there is no controller!


    Now we just wire everything up:

    PHP Code:
    $template = new Template('postList.tpl');
    $posts = new Posts($pdo);

    $model = new EveryPostList($posts);
    $view = new ListView($model$template);

    echo 
    $view->output(); 
    Advantage 1: There is no controller. Controller code is unnecessary by definition when there is no user interaction.
    This part in particular puzzled me, because your "wire it up" code seems to be doing what other frameworks use the controller for -- fetch data, pass it to a template, and render. Except you've eliminated the controller, and now this code lives in some undetermined place. (You vaguely suggested it might live in the router or the front controller.)
    "First make it work. Then make it better."

  6. #6
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    I certainly appreciate all the effort you put in. When I asked for a step-by-step refactoring, I was kinda hoping to start at the first step of identifying the problem -- a concrete example where you lose flexibility or where you have to repeat logic. This looks like you went straight to the last step, your final solution, and skipped the intermediate refactoring steps.



    This part in particular puzzled me, because your "wire it up" code seems to be doing what other frameworks use the controller for -- fetch data, pass it to a template, and render. Except you've eliminated the controller, and now this code lives in some undetermined place.
    The problem is, it's not a controller. By definition "A controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document)." giving it the job of constructing object trees breaks the Single Responsibility Principle which in turn violates separation of concerns, the controller shouldn't be concerned with where its model or view come from.

    As to where it goes, In any framework you need somewhere that initialises the right controller based on the route. You move that logic up to there to the top level of the application. And you'll notice that the view is the only place which has access to the template. The point is separation of concerns. Of course all the same jobs need to be done, the goal is to separate them to give the application more flexibility.


    Edit: This isn't an MVC issue per se, it's a front controller issue, I was just trying to demonstrate why giving the view access to the model is beneficial.

  7. #7
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    The problem is, it's not a controller. By definition "A controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document)." giving it the job of constructing object trees breaks the Single Responsibility Principle which in turn violates separation of concerns, the controller shouldn't be concerned with where its model or view come from.

    As to where it goes, In any framework you need somewhere that initialises the right controller based on the route. You move that logic up to there to the top level of the application.
    We both already know that a framework controller doesn't fit the definition of an MVC controller, so let's forget about the terminology for now. Let's say frameworks have a collection of WireItUp classes. The router defines which WireItUp class should be invoked for a given route, and the front controller ultimately instantiates the WireItUp object. The WireItUp object proceeds to fetch data and render a template.

    This seems to fit what you're describing, yet all I've done is stop using the word "Controller" and replaced it with "WireItUp".
    "First make it work. Then make it better."

  8. #8
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Except that isn't all they do.

    Ignoring the definition of a controller....


    1) Where do you handle user actions? Surely not in your WireItUp object? That's an instant SRP violation.

    2) How do you decide how to initiate the WireItUp object and which one to use for a given route? This has to be done somewhere. Whatever you call it, you need an entry point that makes some decisions on how the top level of the application will work.

    3) The initial discussion was about Views accessing data from models directly which was what I was trying to address. The frameworks you describe all extract some data from the domain model and pass it directly to the view and then output the view. What I was trying to demonstrate was the benefit from a distinction between an MVC model and a domain model as well as a specific view layer. What I was trying to demonstrate was the flexibility added by introducing ViewModels which act as a bridge between the domain and the GUI.

  9. #9
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    Except that isn't all they do.

    Ignoring the definition of a controller....


    1) Where do you handle user actions? Surely not in your WireItUp object? That's an instant SRP violation.
    The only action available to the user is the HTTP request, which means the router, at least in part, handles user actions. And any form processing -- at least in Symfony -- is done in separate form and validation classes. So whereas a WireItUp object would farm that work out to a controller, instead Symfony's controller farms that work out to a form class.

    Quote Originally Posted by TomB View Post
    2) How do you decide how to initiate the WireItUp object and which one to use for a given route? This has to be done somewhere. Whatever you call it, you need an entry point that makes some decisions on how the top level of the application will work.
    With config files. I'd think every application, whether pure MVC or no, needs a place to associate routes with WireItUps/Controllers.

    Quote Originally Posted by TomB View Post
    3) The initial discussion was about Views accessing data from models directly which was what I was trying to address. The frameworks you describe all extract some data from the domain model and pass it directly to the view and then output the view. What I was trying to demonstrate was the benefit from a distinction between an MVC model and a domain model as well as a specific view layer. What I was trying to demonstrate was the flexibility added by introducing ViewModels which act as a bridge between the domain and the GUI.
    I thought the original question was more generally about the relationship between views, models and controllers. Perhaps I was mistaken.
    "First make it work. Then make it better."

  10. #10
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    The only action available to the user is the HTTP request, which means the router, at least in part, handles user actions. And any form processing -- at least in Symfony -- is done in separate form and validation classes. So whereas a WireItUp object would farm that work out to a controller, instead Symfony's controller farms that work out to a form class.
    I disagree. A HTTP request isn't a user action. This highlights a very poor separation of concerns. The MVC triad is for the GUI it should not be concerned whether the user action is triggered by a HTTP request or, like a desktop application, an in-memory pointer between a button on a form to a controller. From a software architecture point of view, there is zero difference between sending this request via HTTP or window messages, callbacks and references to objects in memory. All this stuff is abstracted away in any other language. In a Java MVC applications running on windows you wouldn't worry that the initial request was actually triggered with a win32 API call. The application developer simply should not be concerned with such low level underlying architecture. HTTP is no different.

    As such, none of this has any impact on the merits of PAC vs MVC. From what I can tell, your reasoning for using PAC over MVC is that PAC requires a simpler front controller. Which honestly says nothing about the merits of one architecture over the other.

    With config files. I'd think every application, whether pure MVC or no, needs a place to associate routes with WireItUps/Controllers.
    Exactly my point. You need a config file. Configure the wiring there and skip the "WireitUp" object entirely. All you're left to deal with as an application developer is something to handles the user action, which funnily enough neatly fits the definition of an MVC controller.

    I thought the original question was more generally about the relationship between views, models and controllers. Perhaps I was mistaken.
    It was, but specifically the differences between PAC and MVC. Which in this case is the role of a controller as a gateway between the model and the view.



    It seems to me, you're imposing restrictions on the software architecture based on your own inability to sufficiently abstract the underlying architecture. Which may be a valid reason to pick a certain software architecture, but it certainly doesn't add anything to the debate about which architecture is better because you're not discussing the software architecture (MVC or PAC), you're basing your decision on the limitations you've decided to impose on the underpinning technology.

  11. #11
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    I disagree. A HTTP request isn't a user action.
    How else can the user interact with our application? Everything the user does is translated by the browser into a request. From the perspective of our server-side application, the HTTP request is the only way we get any input from the user.

    Quote Originally Posted by TomB View Post
    As such, none of this has any impact on the merits of PAC vs MVC. From what I can tell, your reasoning for using PAC over MVC is that PAC requires a simpler front controller. Which honestly says nothing about the merits of one architecture over the other.
    Much more than just the front controller is simpler. The view will never need to respond to user input, nor will it need to respond to changes in the model, because there isn't any input beyond the one-time only HTTP request. And a view that doesn't need to respond to anything or update for anything is basically a template. A one-time only rendering. If we try to build a technically correct MVC view that responds to input that will never come and updates for model changes that won't happen, then we'll be building functionality that will never be needed in a server-side web application. Likewise, a technically correct MVC controller would have little or nothing to do in a server-side web application, since there is only one kind of input that happens one time only. PAC seems a much better fit for web applications, and it's no coincidence that that's how all the major frameworks have been built, even if they have the wrong name for the pattern.

    Quote Originally Posted by TomB View Post
    Exactly my point. You need a config file. Configure the wiring there and skip the "WireitUp" object entirely. All you're left to deal with as an application developer is something to handles the user action, which funnily enough neatly fits the definition of an MVC controller.
    So the config file would list what data to fetch and what template to render...? That seems... awkward.

    ... it certainly doesn't add anything to the debate about which architecture is better because you're not discussing the software architecture (MVC or PAC), you're basing your decision on the limitations you've decided to impose on the underpinning technology.
    Well... yeah. We know we're writing web applications. If we wrote our code in such a way that they could become desktop applications with little or no change, then we're going to be spending a lot of effort on functionality that realistically will never be used.
    "First make it work. Then make it better."

  12. #12
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    How else can the user interact with our application? Everything the user does is translate by the browser into a request. From the perspective of our server-side application, the HTTP request is the only way we get any input from the user.
    In a Windows desktop application the only way to get any input from the user is via window messages. There's no such thing as buttons, there's just clicks at co-ordinates on the screen. These underlying events are very dumb. The windows API function calls don't have concepts of "buttons", "objects", "controllers" or any such high level components, simply that a click was registered at certain co-ordinates. It's the technology you're working with, be it .NET, Java, C++, Delphi that translates these low level events into anything that is meaningful to the application and routes it back to the correct memory location for the action that you, as the developer, has registered to trigger when a click is registered at certain co-ordinates.

    From the perspective of our desktop application, the windows event is the only way to get any input from the user.

    This event might contain things like what type of event it was, click, keypress, mouse movement etc but they all come in a very basic format which generally comes in as "The user has done something". Simpler than HTTP even.

    It's down to the technology to abstract these low-level messages into a format that is meaningful to the application. In a Win32 application the creation of a button at a low level would hook into the click event and if the co-ordinates of the click were in the same place as the button it would intercept the event, and call the function that the application developer had chosen to associate with the click. There may be a hundred different callbacks registered to the click event, it's up to some internal decision making that's usually abstracted by the programming language or a library within it to decide which registered callback needs to be triggered. This is exactly what our router does in PHP. This is no different to HTTP, we're told a user "has dome something" and we can send across some relevant information about what they have done.

    You are too concerned with imposing restrictions on yourself because there are some technical challenges to think through. If they're that worrysome, abstract them away and never think about them again!

    In my opinion, you're approaching the problem from the wrong end. You're starting off by deciding the limitations of the technology and picking an architecture which can work within those limitations. My approach is to decide which is the best architecture and then work around any limitations that come up, if they even exist in the first place (Which I'd argue they don't!). As such, take a step back. Which is better for a developer to use: MVC or PAC, or something else? Answer that first. Then you can see if there are any limitations.

    Much more than just the front controller is simpler. The view will never need to respond to user input, nor will it need to respond to changes in the model, because there isn't any input beyond the one-time only HTTP request. And a view that doesn't need to respond to anything or update for anything is basically a template. A one-time only rendering. If we try to build a technically correct MVC view that responds to input that will never come and updates for model changes that won't happen, then we'll be building functionality that will never be needed in a server-side web application. Likewise, a technically correct MVC controller would have little or nothing to do in a server-side web application, since there is only one kind of input that happens one time only. PAC seems a much better fit for web applications, and it's no coincidence that that's how all the major frameworks have been built, even if they have the wrong name for the pattern.

    It's true that we don't need to refresh the view. But this is an optional part of MVC. The key difference between MVC and PAC for us as web developers is that in MVC the view requests data from the model instead of being fed it by the controller. This is for separation of concerns only. Everything else is irrelevant, it makes no difference if the view needs to be refreshed or indeed what it needs to do. It's an issue of pure OOP theory and not one of practicality and is totally unaffected by the underlying techology.


    So the config file would list what data to fetch and what template to render...? That seems... awkward.
    The config would just map a route to the various components. Exactly what any router does only contains metadata about which controller to initiate (if any), which view to initiate and what parameters need to be passed to those components. This allows a true separation of concerns because any component can be easily reused anywhere.

  13. #13
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    You're starting off by deciding the limitations of the technology and picking an architecture which can work within those limitations. My approach is to decide which is the best architecture and then work around any limitations that come up, if they even exist in the first place (Which I'd argue they don't!).
    I think we finally stumbled on the core of this debate: Whether to pick an architecture that is useful in any application environment, or to pick an architecture that is specifically tailored for web applications.

    My preference -- and I'd confidently wager the preference of the vast majority of web developers -- is for the latter. It comes down to effort vs payoff. Our web applications will almost certainly never be used as desktop applications. And if that scenario never materializes, then the payoff never materializes. And if there's no payoff, then it seems like wasted effort.
    "First make it work. Then make it better."

  14. #14
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    That seems like a cop out to me. I've demonstrated several times in several threads including this one how MVC benefits us over PAC as web developers and despite hefty debates, the only responses I get are either philosophical: "The tree fell in the forest while I was away but it's back exactly how I left it every time I return. I don't consider it a real tree." or argumentum ad populum: "Everyone else does it this way". What I've yet to see is anyone show the benefits of PAC over MVC or point out any real-world issues with applying MVC proper on the web.

  15. #15
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    I've demonstrated several times in several threads including this one how MVC benefits us over PAC as web developers...
    You've posted lots of code, but it still isn't clear to me where the benefits appear. That's why I wanted to see step-by-step refactoring that starts from a concrete example that demonstrates the problem your code attempts to solve.

    Quote Originally Posted by TomB View Post
    ...and despite hefty debates, the only responses I get are either philosophical: "The tree fell in the forest while I was away but it's back exactly how I left it every time I return. I don't consider it a real tree." or argumentum ad populum: "Everyone else does it this way". What I've yet to see is anyone show the benefits of PAC over MVC or point out any real-world issues with applying MVC proper on the web.
    Pretty sure my points of view never involved a tree. And even in retrospect, I think my responses have been very concrete (not philosophical), such as effort vs (lack of) payoff, asking where wire-up code is supposed to live, and asking for step-by-step refactoring.
    "First make it work. Then make it better."

  16. #16
    SitePoint Member
    Join Date
    Feb 2009
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is an interesting conversation but I feel the debate about PAC versus MVC is not just of web versus non-web but also about type of web project.

    Web projects come in many forms, you have product development which can be software as a service and is long life cycle with many hands versus build release and forget development( short running microsites etc. ). Programming practices for both are not completely interchangeable due to the type of development pace they promote and types of debt they incur( rapid prototype versus long term feature change stable ).

    Short fire and forget development by its nature can often ignore the pay the debt phase that long term development has to do to stay alive so some practices can turn seriously detrimental to the health of a product by killing velocity.

    My main problem with all frameworks except the micro frameworks is that to be popular they try and cover both these bases leaving a pretty schizoid design process and a lot rules have to be applied at a human team level of what can be done done and how it should be done. This in turn is very tiring as it leads to a lot of whys, actually it is far more hassle than say Zend 1 as an MVC framework actually solves.

    What should be focussed on is as a business owner which methodology promotes the highest truck/bus number http://en.wikipedia.org/wiki/Bus_factor and also promotes the ability to unit test versus integration test as that can be the difference between 10 minutes or 3 hours after coding finished to release to an integration environment causing development traffic jams. As developers we serve businesses so we need to think about long term team productive management versus initial release point so we sometimes need think more like business owners.

    I haven't had much sleep as today I have to feature cost on a balls up of a Zend framework project( a lot due to the practices it promotes such as helper brokers being used as some form of monkey patching dependency inversion technique ) and whenever I think about that project I get very angry so my brain is not at its best coherance. So my question is Tom what benefits does PAC give and what benefits does it take away and in contrast what does a purer MVC offer and also what does it take away?

  17. #17
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    You've posted lots of code, but it still isn't clear to me where the benefits appear. That's why I wanted to see step-by-step refactoring that starts from a concrete example that demonstrates the problem your code attempts to solve.
    The problem with step by step refactoring of that problem is that you're starting off with an example that has made a lot of assumptions and there are a thousand steps. Even the symfony example page takes a lot of code/writing to explain how to get to a very simplistic point. The problem is, the differences between PAC and MVC don't show themself until after that point.

    Consider this endpoint from that page:

    PHP Code:
    // src/Acme/BlogBundle/Controller/BlogController.php
    namespace Acme\BlogBundle\Controller;

    use 
    Symfony\Bundle\FrameworkBundle\Controller\Controller;

    class 
    BlogController extends Controller
    {
        public function 
    listAction()
        {
            
    $posts $this->get('doctrine')->getManager()
                ->
    createQuery('SELECT p FROM AcmeBlogBundle:Post p')
                ->
    execute();

            return 
    $this->render(
                
    'AcmeBlogBundle:Blog:list.html.php',
                array(
    'posts' => $posts)
            );
        }

        public function 
    showAction($id)
        {
            
    $post $this->get('doctrine')
                ->
    getManager()
                ->
    getRepository('AcmeBlogBundle:Post')
                ->
    find($id)
            ;

            if (!
    $post) {
                
    // cause the 404 page not found to be displayed
                
    throw $this->createNotFoundException();
            }

            return 
    $this->render(
                
    'AcmeBlogBundle:Blog:show.html.php',
                array(
    'post' => $post)
            );
        }

    This is basic PAC. The controller queries the model and passes the result to the view. There are several issues here, I'll try to go through them step by step.

    Let's add a listByUserAction that lists all the posts by a specified user:

    public function listByUserAction($userId)
    {
    $posts = $this->get('doctrine')->getManager()
    ->createQuery('SELECT p FROM AcmeBlogBundle:Post p WHERE p.userId = ' . $userId)
    ->execute();

    return $this->render(
    'AcmeBlogBundle:Blog:list.html.php',
    array('posts' => $posts)
    );
    }
    As you can see it involves a lot of repeated code. In actual fact the only thing that's changed is the query.


    To use that same logic with a different template, I have to repeat the logic yet again:

    PHP Code:
        public function listWithAdifferentTemplate($userId)
        {
            
    $posts $this->get('doctrine')->getManager()
                ->
    createQuery('SELECT p FROM AcmeBlogBundle:Post p WHERE p.userId = ' $userId)
                ->
    execute();

            return 
    $this->render(
                
    'AcmeBlogBundle:Blog:list-alternative.html.php',
                array(
    'posts' => $posts)
            );
        } 
    Here all the data-fetching logic has been repeated. And I need to do it a fourth time if I want the full list with a different template.


    Problems with this system:

    1) I add a "published" flag that each user can set when a blog is added to decide whether it's visible or not. I now need to go through an manually edit each query because I repeated it.

    2) I add a second variable "heading" to the template. I now need to go through every single method that's using the template, find what the variable should be and supply it to the template.


    3) Common logic for things like pagination, form processing and listing records must be repeated every time it's needed rather than defining it once and reusing it.

    4) How do I reuse this code? I want to display two lists of posts, use my example from before, comparing the posts of two different users side by side. It's simply not easy. The most convenient method is doing two queries in the controller and passing two data sets to the view. Repeated code galore.

    5) With this code in particular, it breaks the Law of Demeter. Why is a Doctrine object being searched for by the controller (Tell, don't ask). Why does it need to inherit from a base class? This class has a horribly weighty API: http://api.symfony.com/2.0/Symfony/B...ontroller.html Considering most controllers will never use most of these methods it's very poor Separation of Concerns.


    How does MVC proper fix these problems?

    1) The query and all the logic surrounding it is stored in one reusable place, the MVC Model and it's fully interchangeable without affecting anything else. I demonstrated this earlier in the topic. Essentially it becomes a change in one place instead of a change in every place.

    2) Because there's less repeated code, this is a change in one place. The MVC Model. I change any models which will be used with that template. Because this model is reusable it's one change per model, rather than one change per instance of the template. Secondary to that... in the Symfony example, how do I find everywhere that's using the template? In MVC everything is API based so I can extend the interface:

    PHP Code:
    interface ListViewModel {
         public function 
    getRecords();
         public function 
    getHeading();

    Now I know that any class which implements ListViewModel will need an additional method adding. In fact, my IDE, Zend Studio even picks this up for me and highlights anywhere I've defined a class that implements ListViewModel and doesn't supply all the methods. I don't have to traipse around the entire codebase looking for duplication of: "return $this->render(
    'AcmeBlogBundle:Blog:list-alternative.html.php',
    array('posts' => $posts)
    );"

    and potentially adding some code to resolve $heading, then supply $heading to the template.


    3) This logic goes in the view and is fully reusable. Pagination is a good example. How do you do it in symfony? Repeat the logic in every controller/template because you don't have a proper view.

    4) I demonstrated this earlier. Because the controller isn't doing view selection, Views can be used without a controller and very easily nested. And because they have their own instance of a model with its own state, two instances of the same view can easily be created with two instances of the model and everything is reusable with ease. I'm genuinely interested in how this is achieved in PAC. Every example I've seen has involved either ugly hacks using inheritance, controller action chaining or creating a new instance of the whole triad within the controller. And what's worse, because the controller has access to GET/POST it's likely that it's using POST['user'] to decide which user information to show. This is an instant killer of reusability.

    5) This is more than likely just poor design on Symfony's part but it goes a long way in demonstrating the competence of the developers and if they can't understand why Law of Demeter violations are bad or why inheritance trees should be avoided, are they really in a position where they are able to fully comprehend the subtle differences between MVC and PAC? Probably not. They probably used PAC because they copied everyone else rather than thinking about what they were actually trying to achieve. In fact, I suspect that they don't even know what MVC is. They used PAC because they'd learnt MVC from another web framework and hadn't even considered the prospect of giving the view access to the model. Once again it seems that the PAC architecture exists here entirely accidentally rather than due to any kind of informed decision making process.



    Pretty sure my points of view never involved a tree. And even in retrospect, I think my responses have been very concrete (not philosophical), such as effort vs (lack of) payoff, asking where wire-up code is supposed to live, and asking for step-by-step refactoring.
    I've yet to see any demonstration of how MVC is more effort. In fact my point was that it reduces complexity but making components reusable and removing the binding logic and repeated code entirely. Answer this: Why is PAC better than MVC?

  18. #18
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    I've officially split the original thread into two threads, primarily because the topic changed focus to MVC vs PAC and I think that makes sense as a thread of its own. Plus I feel the OP was afraid to post back after the debate got started, so hopefully cleaning up his original thread will grant him the option of returning and asking any additional questions about his MVC/PAC implementation.

    I do hope this conversation continues as there is a lot of good information here that many will be happy to read.

    Thanks,
    cpradio
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  19. #19
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    Even the symfony example page takes a lot of code/writing to explain how to get to a very simplistic point.
    Nonetheless, it seemed like our nu-uh/ya-huh discussion would continue indefinitely on these forums unless one day we can settle it with simple, concrete, step-by-step refactoring examples. You could even reuse whatever you write for your site.

    Quote Originally Posted by TomB View Post
    Let's add a listByUserAction that lists all the posts by a specified user:
    As you can see it involves a lot of repeated code. In actual fact the only thing that's changed is the query.
    I see and acknowledge that repeated code. Though, I'm skeptical that the best solution is rearchitecting the whole system. It seems to me that common code between any two controller actions could be resolved simply with a private method within the controller. For example, if you wanted to isolate the list rendering code or the data fetching code:

    PHP Code:
    class BlogController extends Controller
    {
        
    // ...

        
    private function renderList($items)
        {
            return 
    $this->render(
                
    'AcmeBlogBundle:Blog:list.html.php',
                array(
    'posts' => $items)
            );
        }

        private function 
    getPostsByUserId($userId)
        {
            
    $posts $this->get('doctrine')->getManager()
                ->
    createQuery('SELECT p FROM AcmeBlogBundle:Post p WHERE p.userId = ' $userId)
                ->
    execute();

            return 
    $posts;

            
    // A little further into the refactoring process,
            // and Symfony projects would actually move this data fetching code
            // into a PostRepository class
        
    }

    Quote Originally Posted by TomB View Post
    3) Common logic for things like pagination, form processing and listing records must be repeated every time it's needed rather than defining it once and reusing it.
    Pagination can be and is reused. In templates, it would look something like this:

    Code:
    {{ pager.render('AcmeBlogBundle:Blog:pagination.html.twig') }}
    Form processing can be and is reused. The form itself and validation rules are defined in their own classes, so all the controller has to do is:

    PHP Code:
    if ($form->isValid()) {

    In all these cases, when we run into any code repitition, we simply isolate the common parts into their own method or their own class, which I think is pretty standard refactoring. So now the question is: would rearchitecting the whole system make this easier? And I, at the very least, would like/need to see how that refactoring would progress.

    Quote Originally Posted by TomB View Post
    4) How do I reuse this code? I want to display two lists of posts, use my example from before, comparing the posts of two different users side by side. It's simply not easy. The most convenient method is doing two queries in the controller and passing two data sets to the view. Repeated code galore.
    Now that we've isolated the data fetching code, I think it's straightforward how you would reuse it. Your own code, in fact, also makes two different queries, and it does so in your WireItUp code, which we already know is analogous to a framework controller. Moreover, your own code also passes two data sets to the view. Your compareTwoUsers template is passed "user1" and "user2".

    Quote Originally Posted by TomB View Post
    5) With this code in particular, it breaks the Law of Demeter. Why is a Doctrine object being searched for by the controller (Tell, don't ask). Why does it need to inherit from a base class? This class has a horribly weighty API: http://api.symfony.com/2.0/Symfony/B...ontroller.html Considering most controllers will never use most of these methods it's very poor Separation of Concerns.
    Normally I'd have to grant you this one, but in Symfony, extending the base controller is completely optional, and it exists only for convenience. The Symfony docs/book goes on to describe how you can define your controllers as standalone, injectable objects. My impression is that not many people using Symfony bother to do that, and the reason, I suspect, is once again effort vs payoff. An injectable object is typically easier to unit test, but in this case, unit testing a controller pretty much means you're actually doing functional testing, and it's actually more convenient to fill your database with test data than it is to create a mock doctrine object for injection. A standalone, injectable controller would also be easier to transplant into any other framework, but since no one ever really intends to even try that, this is another payoff that will never materialize. And so people typically opt for the convenience of the base controller.

    Quote Originally Posted by TomB View Post
    How does MVC proper fix these problems?
    1) The query and all the logic surrounding it is stored in one reusable place
    Ditto in Symfony.

    Quote Originally Posted by TomB View Post
    How does MVC proper fix these problems?
    2) ...this is a change in one place. The MVC Model. I change any models which will be used with that template.
    Ditto in Symfony.

    Quote Originally Posted by TomB View Post
    How does MVC proper fix these problems?
    3) This logic goes in the view and is fully reusable. Pagination is a good example. How do you do it in symfony? Repeat the logic in every controller/template because you don't have a proper view.
    In Symfony, pagination logic exists in one place within the controller, and one place within the template. I showed above how query logic can be isolated, and templates can use either "include" or "render" to reuse template partials or to reuse the output from other controllers.

    Quote Originally Posted by TomB View Post
    How does MVC proper fix these problems?
    4) ...Because the controller isn't doing view selection, Views can be used without a controller and very easily nested.
    Ditto in Symfony.

    Quote Originally Posted by TomB View Post
    How does MVC proper fix these problems?
    5) This is more than likely just poor design on Symfony's part but it goes a long way in demonstrating the competence of the developers and if they can't understand why Law of Demeter violations are bad or why inheritance trees should be avoided
    Based on all the above, it seems there's an awful lot about Symfony you didn't know, and your assumptions about it thus far simply haven't been accurate.
    "First make it work. Then make it better."

  20. #20
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    So basically it's an OOP vs Procedural debate now? Chaining methods in this manner sacrifices: Polymorphism - I can't substitute a method with another method very easily (I need to use inheritance just to achieve this). And Encapsulation - The application state is shared among every controller action.


    I see and acknowledge that repeated code. Though, I'm skeptical that the best solution is rearchitecting the whole system. It seems to me that common code between any two controller actions could be resolved simply with a private method within the controller. For example, if you wanted to isolate the list rendering code or the data fetching code:
    The problem here is that as far as I'm aware, most programmers are not clairvoyant. Look at the example from earlier, it wasn't written this way to begin with and in most cases wouldn't ever be. You, the application developer has had to go back and modify code which is currently working 100% for its original goals. You've gone back and re-structured the entire application based on the needs of a change which happend after-the-fact. This means re-testing all your old code and making sure you haven't broken anything.

    As I said, it's an OOP vs procedural debate at this stage. In OOP it's easy to substitute any part of the application. Now you've moved it into a procedural level by passing state between functions this isn't possible without hacks such as inheritance (Which isn't the right tool for this job, and creates immense scalability issues). Try to completely change the implementation of getPostsByUserId() for a specific controller but keep everything else the same. Nope. Not without a dodgy workaround or changing the controller code and repeating logic.


    Pagination can be and is reused. In templates, it would look something like this:
    Forgive me, but that code is far from self explanatory. Pagination logic involves knowing the total number of records, the number of records to display per page and what page is currently being viewed. I can see none of that there.

    Form processing can be and is reused. The form itself and validation rules are defined in their own classes, so all the controller has to do is:
    It's not just the processing though, it's the display logic surrounding the form. The logic for whether the view should show any errors, what variables are available in the view, etc.


    Now that we've isolated the data fetching code, I think it's straightforward how you would reuse it. Your own code, in fact, also makes two different queries, and it does so in your WireItUp code, which we already know is analogous to a framework controller. Moreover, your own code also passes two data sets to the view. Your compareTwoUsers template is passed "user1" and "user2".
    Two queries are run. Only one is actually coded. There is a difference here, it's subtle i'll admit but it's important. The distinction is that each MVC triad has its own state and can be isolated within the application. At the same time, any part of either of the triads can be substituted for something else. I could be, for instance, comparing the posts in one database to the posts in another by substituting the model in one of the triads. There would be 4 classes defined, two models, one controller and one view. Both instances of the view would be of the same class (but different instances), both instances of the controller would be the same class (but different instances) and the two models differ entirely, they could have different dependencies, different configurations, anything. But since they're loosly coupled to everything else and abide by encapsulation rules, this isn't a problem.

    This is where the flexibility happens. Because OOP affords us Encapsulation and Polymorphism, I can re-use and substitute in and out any of the components with ease. Using procedural style methods in a "controller" class limits this flexibility. You're tied into an implementation because it has to be $this->method(). You can work around that in a very ugly way with inheritance but your controller is still forced to know of all the dependencies used by any of the methods. In my database example, it needs access to a model which accesses the first database and a model which accesses the second database. Not good!

    This is messy. It reduces portability because nothing is isolated. I can't just copy certain features between projects because everything is tied together. In my other project I want the list posts by users funtionality but there is only one database so I the compare 2 databases method wont work... and I can't even initiate the class because the second dependency doesn't exist.

    It's very basic OO theory: A class should have a single responsibility. It affords it reusability and will inevitably mean it has fewer dependencies making it portable as well.

    Normally I'd have to grant you this one, but in Symfony, extending the base controller is completely optional, and it exists only for convenience. The Symfony docs/book goes on to describe how you can define your controllers as standalone, injectable objects. My impression is that not many people using Symfony bother to do that, and the reason, I suspect, is once again effort vs payoff. An injectable object is typically easier to unit test, but in this case, unit testing a controller pretty much means you're actually doing functional testing, and it's actually more convenient to fill your database with test data than it is to create a mock doctrine object for injection. A standalone, injectable controller would also be easier to transplant into any other framework, but since no one ever really intends to even try that, this is another payoff that will never materialize. And so people typically opt for the convenience of the base controller.
    I'd argue the reason that few people do this because Symfony controllers have too many responsibilites (See Flaw: Class Does Too Much. Each controller action in Symfony is performing a single task. In OOP this should be its own object. And why does a controller have a render method?! That's absurd. It's clearly view logic!
    Why is it view logic? Because the controller should not be concerned with any implementation details about the view. As it stands, the controller initiates the view which means it's impossible to use a different template system without modifying the controller! That is truly ridiculous.



    Ditto in Symfony.
    I think you missed the point here.. but again it's an OOP vs procedural debate.



    In Symfony, pagination logic exists in one place within the controller, and one place within the template. I showed above how query logic can be isolated, and templates can use either "include" or "render" to reuse template partials or to reuse the output from other controllers.

    This was the point I was getting at. "Template partials" are pointless because you have to supply the data to them, which means fetching it from somewhere and repeating the data fetching logic. I want to pull in a stateful object. I want a reusable component. I want to call a simply functon such as $this->getView('/user/details/123'); from any other view and without any additional work have it pull in a table of details for a specific user.

    Actually, this hits the nail on the head. This is why using a controller action to configure the application is a bad idea. It's not easily reusable so you can't instantly create a MVC triad with the same configuration from anywhere, it's tied to the outside world.


    Ditto in Symfony.
    This was about Views being reusable. They simply ARE NOT in Symfony. You must supply the data every single time you use a view which means at worst re-writing the data fetching logic or at best re-writing the binding logic each and every time. In MVC you create the views and pass it a model and everything just works.



    Based on all the above, it seems there's an awful lot about Symfony you didn't know, and your assumptions about it thus far simply haven't been accurate.
    I don't claim to be an expert at Symfony, I don't like it because it uses PAC. That's what I have a problem with, Symfony itself is irrelevant.

  21. #21
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    So basically it's an OOP vs Procedural debate now? Chaining methods in this manner sacrifices: Polymorphism - I can't substitute a method with another method very easily (I need to use inheritance just to achieve this). And Encapsulation - The application state is shared among every controller action.
    OOP doesn't mean that using methods to consolidate repetition within a class suddenly became a faux pas. We're getting back into yu-huh/nu-uh territory, so I'm going back to my original request: Please provide step by step refactoring that illustrates your case. When you (if you) do so, please approach it as if you were reporting a software bug. Start by showing a dead simple example, as simple as you can make it, that demonstrates the issue, then show your step by step refactoring to resolve that issue.

    Quote Originally Posted by TomB View Post
    Try to completely change the implementation of getPostsByUserId() for a specific controller but keep everything else the same. Nope. Not without a dodgy workaround or changing the controller code and repeating logic.
    That "dodgy workaround" would be calling a different method to get a different result. Dodgy? And while I would have to edit the controller code, you too would have to edit your WireItUp code.

    Quote Originally Posted by TomB View Post
    Two queries are run. Only one is actually coded. There is a difference here.
    Only one need be coded in Symfony as well, so... no difference. Remember earlier how straightforward it was to isolate the data fetching logic? Just because your idea of using Symfony is to copy-paste doesn't mean that's how it's actually done.

    Quote Originally Posted by TomB View Post
    ...any part of either of the triads can be substituted for something else. I could be, for instance, comparing the posts in one database to the posts in another by substituting the model in one of the triads.
    Ditto in Symfony. Remember that we have the option of injecting dependencies into our controllers. So we could have two doctrine objects to represent the two databases and inject them respectively into two instances of the same controller class.

    Quote Originally Posted by TomB View Post
    This was the point I was getting at. "Template partials" are pointless because you have to supply the data to them, which means fetching it from somewhere and repeating the data fetching logic.
    Your logic is starting to make less and less sense to me.

    Yes, templates are supplied data. That's how PAC works. No, that does not make them pointless. Yes, displaying data means you must fetch data. No, you do not have to repeat data fetching logic.

    If you're going to continue criticizing Symfony, then I highly recommend you first build something in Symfony (and ask questions), because, frankly, you don't seem to have any clue how it works.
    "First make it work. Then make it better."

  22. #22
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    As I have said, my issue is not with Symfony in particular. I have no interest in using it because the PAC architecture is problematic. I've still yet to see you attempt to answer the question "Why is PAC better than MVC?"

    OOP doesn't mean methods suddenly became off limits. We're getting back into yu-huh/nu-uh territory, so I'm going back to my original request: Please provide step by step refactoring that illustrates your case. When you (if you) do so, please approach it as if you were reporting a software bug. Start by showing a dead simple example, as simple as you can make it, that demonstrates the issue, then show your step by step refactoring to resolve that issue.
    Unfortunately I had hoped anyone participating in a discussion at this level would have an understanding of the difference between procedural and oop and understand the problems created by introducing dependencies in code.

    Let's take a step back, away from frameworks, away from everything back to the fundamental difference between OOP and Procedural programming.


    Let's create a basic function that formats a unix timestamp as a date using a specified format:

    PHP Code:
    function formatDate($format$timestamp) {
        return 
    date($format$timestamp);
    }


    $formattedDate formatDate('d/m/Y'time()); 
    In OOP this would be expressed as:

    PHP Code:
    class Date {
        private 
    $timestamp;

        public function 
    __constuct($timestamp) {
            
    $this->timestamp $timestamp;
        }

        public function 
    format($format) {
            return 
    date($format$this->timestamp);
        }
    }

    $date = new Date(time());

    $formattedDate $date->format('d/m/Y'); 


    OOP is more code, but there is a key difference: encapsulation. What this means is that the part of the code which is concerned with formatting the date is not concerned with locating the timestamp.


    As such, the constructed $date object can be passed around the system with along with its state.


    The procedural method is stateless this means it needs to be passed all its parameters every time. The knock on effect here is that anywhere that calls the formatDate() function has the responsibility of providing both the timestamp and the date format.



    A procedural template would look like this:

    PHP Code:
    <p>The date is: <?php echo formatDate('d/m/Y'$timestamp); ?></p>
    Whereas the OOP version would look like this:

    PHP Code:
    <p>The date is: <?php echo $date->format('d/m/Y'); ?></p>
    The practical difference here is minimal, isn't it? In both cases, a single variable has been bound to the template.

    However, from a flexibility perspective, it's not great. formatDate() can only take timestamp as a parameter. What if I had a system where some of the dates were in a string format of 'YYYY-DD-MM'?

    With the procedural method, I'd have to create a stringToTimestamp() method like so:

    PHP Code:
    function stringToTimeStamp($string) {
        list(
    $y$m$d) = explode('-'$string);
        return 
    mktime(000$m$d$y);

    And every time I came across a date, I'd have to convert it:

    PHP Code:
    <p>The date is: <?php formatDate('d/m/Y'stringToTimeStamp($strDate)); ?></p>

    The problem? I had to alter the template* to know that the date is coming in as a string not a timestamp.

    * Yes, this could be done when it's assigned to the template, but that moves the problem to the binding logic. The conversion must be done somewhere.



    OOP avoids this problem entirely:

    PHP Code:
    class StrDate {
        private 
    $string;

        public function 
    __construct($string) {
            
    $this->string $string;
            
        }

        public function 
    format($format) {
            list(
    $y$m$d) = explode('-'$this->string);
            return 
    date($formatmktime(000$m$d$y));
        }


    I can now pass a StrDate object to the template and the OO template works without being changed, as does the code which is binding variables to the template.




    Hopefully that's a simple enough demonstration of OOP vs Procedural. Now, back on track... your code is procedural and not oop not because it's using methods, but because your class is self-configuring and stateless.


    Consider this:

    PHP Code:
    class Date {
        private function 
    getCurrentDate() {
            return 
    time();        
        }


        private function 
    format($date$format) {
            return 
    date($format$date);
        }


        public function 
    output() {
            return 
    $this->format($this->getCurrentDate(), 'd/m/Y');

        }

    This is the important part: This is functionally identical to the BlogController you posted: The state is generated internally on the fly every time output() is called and passed around internally, it never truely exists beyond the life of a call to $date->output(). This class can only ever output the current date. Compare this to your BlogController and at this basic level it's identical: The class is self-configuring and has no real state.

    Again, this is a very important distinction. If you cannot understand this point, then it's not worth continuing this discussion at all.



    Hopefully, this is a clear enough example. Now, this is procedural because it is identical to:


    PHP Code:
    function getCurrentDate() {
        return 
    time();        
    }


    function 
    format($date$format) {
        return 
    date($format$date);
    }

    function 
    output() {
        return 
    format(getCurrentDate(), 'd/m/Y');



    The problem this causes is quite large. Going back to the non-oo date class: How can I substitute the getCurrentDate() function?


    PHP Code:
    class Date {
        private function 
    getCurrentDate() {
            return 
    time();        
        }


        private function 
    format($date$format) {
            return 
    date($format$date);
        }


        public function 
    output() {
            return 
    $this->format($this->getCurrentDate(), 'd/m/Y');
        }


    Because it's calling $this->format() internally it will ALWAYS call Date::getCurrentDate(). $this will always resolve to a Date object.


    The only way this can be achieved is inheritance. I don't want to change the code because it's in use elsewhere and needs its current implementation.


    So...


    PHP Code:
    class TomorrowsDate extends Date {
        private function 
    getCurrentDate() {
            return 
    strtotime('+1 day');    
        }

        


    This works, doesn't it?

    What if I want to change the date format in the output function for a specific instance of TomorrowsDate (Not all of them, mind!), I have to extend, again:

    PHP Code:
    class TomorrowsDateAsMySql extends TomorrowsDate {
        public function 
    output() {
            return 
    $this->format($this->getCurrentDate(), 'Y-M-D');
        }



    But what if I want a normal date class formatted as MySQL.. now I need to extend the base Date class again!


    PHP Code:
    class DateAsMysql extends Date {
        public function 
    output() {
            return 
    $this->format($this->getCurrentDate(), 'Y-M-D');
        }



    And there we have repeated code! And a very messy, hard to understand class inheritance hierarchy.


    The problem with this, and your controller examples is that the objects have no real state. They're self-configuring and will only ever get configured in a very specific way. Their transient state only exists for the duration of one method and not beyond that, and because the state is dictated internally, it makes it impossible to substitute parts of the system.


    Now imagine if we were't using $this in the non-oo Date class but intead calling a method on another class and the pieces should fit together.


    I hope that's simple enough. I really can't make it any simpler. It really is a case of OOP vs procedural.

  23. #23
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    After reading that back I'd like to clarify, the biggest issue is with $this. Because it can only ever resolve to a very specific implementation, it is from a theoretical point of view, static. There's no way to substitute the meaning of $this at runtime. With any other variable, such as $this->foo, the meaning of $this->foo can be substituted at any time, it can point to anything. $this cannot, it must always resolve to an instance of the current class. And that is why it is, for practical purposes, procedural. $this->query() locks you into a specific implementation just as much as mysqli_query does. Whereas using $this->db->query(); gives you access to whatever query method is provided by whatever $this->db happens to be... and that can be any class at all, it's not confined to a single implementation.

  24. #24
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,246
    Mentioned
    16 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    ...With any other variable, such as $this->foo, the meaning of $this->foo can be substituted at any time, it can point to anything. $this cannot, it must always resolve to an instance of the current class. And that is why it is, for practical purposes, procedural. $this->query() locks you into a specific implementation...
    So is it now a blunder for a class to ever call one of its own methods? Is $this->anyOtherMethod() also bad? I had suggested that a class could create a private method to consolidate repetitious code found within itself. Apparently this earned me TomB's Doesn't Understand OOP trophy.
    "First make it work. Then make it better."

  25. #25
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Not at all, calling internal methods is fine, but you have to understand that it's going to call a very specific implementation. This is usually desirable behaviour. As with anything in programming, there's a time to use them and a time to avoid them. If it would ever be beneficial to substitute that method for a different implementation, then using $this->method() is not the best candidate for the job. Much as Class::method() isn't.


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
  •