SitePoint Sponsor

User Tag List

Page 1 of 5 12345 LastLast
Results 1 to 25 of 106
  1. #1
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    code separation / mvc / need real-life advice

    Hi

    This is my first post here. I'm currently working on a bit complex website/application and I've came to the point, where it's getting harder to maintain the code. I've been reading a lot about design pattern and MVC but I'm only more frustarted with it. There are almost no real-life examples.

    Although I understand the general concept behind it, I'm sitting in front of my code and don't know exactly what to change.

    Here's the short list of things I'm most frustrated with in a moment

    Some general information about the app. There is a frontend, basically the website with information, registration, logging, shop etc. and a backend with amdinistration stuff

    Now, the site is multilanguage (4 languages), the translations are stored in the database.

    Each page contains search box and login box (user can log in on any page and can search information on any page). So, when user jumps to the registration page, he can see 3 forms (login, search and register) and use any of them.

    1. How to handle such situation? Some selected tasks:
    a) I have to render 3 forms + the rest of the layout (navigaion, foot etc)
    (how to combine all the views on 1 page??)
    b) I have to check what form user has used (ok, i know how to do it) and need to perform an action, show error/ok message, render ther rest
    (where to manage sessions ? I have session in the database too)
    c) I have to query the DB and get the translation according to the selected langauge and translate all the elements that need it (form labels, messages, menus)
    (where to put translation query??? view? but there's no DB object!)
    d) Form can contain select boxes with data from different tables (countries list, cities list etc)
    (how should I fetch data from different models???)
    e) I would like to cache queries, cache forms (initial states) and some of the templates
    (I have a cache object. How to use in in different parts of the app?)

    Now, although I managed to do it, it's not quite elegant. There's no separation, no real components I could easily use in the next project. Everything is too much tied to each other, lot of inheritance instead of composition (like in pear).

    I tried different ways to slice the app but I just can't do it. I'm really frustrated and feel dumb...

    Any tips and help would be appreciated

  2. #2
    SitePoint Addict
    Join Date
    Jan 2005
    Location
    United Kingdom
    Posts
    208
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think your first step is to look at separation of concerns. MVC is all about separating business and presentation logic. The model is usually concerned with the database in a web app, looking at your description you might have User and Search models, and several views such as Registration, Search Results etc, with a RegisterController and SearchController tying them together. I'm not exactly sure of the best way to begin refactoring your app, I think I'd start with the domain objects and build out from there.

    When composing Views I am quite happy with scriplets which are just included into the View, some folks here advocate the Composite View pattern though

    Once you have a nice separation between the layers caching usually becomes quite trivial.

  3. #3
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Shrike,

    Thanks for the answer.
    Yeah, I just reverted to the old code base, because there is some time pressure.
    I'd like to create a new branch for this project and work on the new design 'in the background', hopefully merging it some day with the trunk...

    The biggest problem for me is the glue between all those objects. I can find zillion articles talking about MVC but most of them is just theory with 'class Car' examples. I'm fine with the theory, but the practice is not so straightforward...

    Caching is trivial, but when I want to cache data in different layers (queries in Models, templates in Views), then I'm not really sure how should I do it. Should I use separate cache objects (assuming I'll use some Cache class) ? Etc...

    And what about my translation table ;-) I just don't have a clue what layer it belongs to in the MVC pattern...

    This is actually the first time I feel THAT powerless... arghh

  4. #4
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Assuming your current pages are intermingled html and php logic, yoiu could start by separating presentation from the lot. Basically, split your file into two. One containing all the logic, and one containing html with only minimal php logic within. Lets call the first one the controller and the second one the view. As a rule of thumb, anything which requires you to use variables, is too complex for the view. When you encounter that, put it in a function which you place in the controller. As an example, your translation mechanism could be implemented as a function which you call in the view. So rather than something like this :
    stuffpage.php
    PHP Code:
    $lang = array();
    $q = mysql_query("select key, value from lang where lang = ".mysql_escape_string($_SESSION['lang']));
    while ($row = mysql_fetch_assoc($q)) {
        $lang[$row['key']] = $row['value'];
    }
    ...
    <h1><?php echo $lang['stuff']; ?></h1>
    ...
    You'll do something like this :
    stuffpage.php
    PHP Code:
    function translate($key)
      
    $q mysql_query("select value from lang where lang = '".mysql_escape_string($_SESSION['lang'])."' and key = '".mysql_escape_string($key)."'");
      
    $row mysql_fetch_assoc($q);
      return 
    $row['value'];

    view.php
    PHP Code:
    ...
    <h1><?php echo translate('stuff'); ?></h1>
    ...
    (Ignore any though about performance at this point - you'll optimize later).

    The immediate benefit of this change may not be appearent, but now the view has become ignorant of the underlying datasources (aka the model).

    Things could be implemented more elegantly - for starters, you might want to implement your controller as an object, to avoid nameclashes of functions. With the design above, you also have no clear separation of controller and model, which you might want - esp. if your model is complex.

  5. #5
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, thakns for the answer

    Quote Originally Posted by kyberfabrikken
    Assuming your current pages are intermingled html and php logic, yoiu could start by separating presentation from the lot.
    Actually, my presentation is separated from the logic. I have no business logic in presentation layer. I'm currently using Smarty for templates, but I'll drop it in the next project in favour of pure html templates.

    Now, my code looks rather like this (simplified for that post)

    main.tpl
    PHP Code:
    {include file="header.tpl"}

    <
    h1>{$page->translation['HEADER']}</h1>

    {include 
    file="footer.tpl} 
    page.inc (I'm skipping all error checking, escaping etc)

    PHP Code:
    class Page {

    var 
    translation;
    var 
    $db;
    var 
    $lang;

    function 
    addTranslation($group) {

    $sql 'select word_'.$this->lang.' from translation_view where ident = '.$group;
    $result $this->db->queryAll($sql);
    $this->translation += $result;

    }

    function 
    display () {

    echo 
    $this->Smarty->fetch('main.tpl');

    }


    index.php

    PHP Code:

    require page.inc;

    $page = &new Page();

    $page->addTranslation('REGISTRATION');
    $page->addTranslation('LOGIN_FORM');

    $page->display(); 
    This is just a basic example of how it generally looks like. There is more ineritance etc of the Page class from same generic classes.
    Register_Page inherits from page and adds some stuff for managing the form, Page itself contains stuff for loggin/search (as it is th same for each page) etc.

    Each translation in the database is attached to the corresponding group, because there will be hundreds of rows. So I want only those translations that are actually needed. Each component can call addTanslation and add stuff to the 'stack', so the translation variable contains all the things needed by templates (and some form generating stuff)

    As I stated before, I'd like to copy this project to the new branch and start refactoring ASAP. I'll go concarently with the current version though.

    For now, I'd like to split the index page which consists of several components (login box, search box, newsletter box, featured products, some other static stuff). Each component can use specified language in it's presentation layer (including messages like errors in the form etc) (I guess some controller should set up the language and provide it for all components), each component has it's own small template, which is the glued to the main template (or put into). No optimising for now, no caches etc. And honestly. I have no idea how to split this stuff correctly, so they are reusable and usable at the same time

    Sorry for such a long post. Thanks

  6. #6
    SitePoint Zealot
    Join Date
    Jun 2004
    Location
    Norway - Oslo
    Posts
    198
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have recently handled some translation issues, for the project in question we dont use MVC but thats not the point. We have our own template system and what i basicaly did was to create a TemplateLanguage component that was automagicaly glued into the template by the templatesystem itself, all i need to do is choose language when creating the Template object. Our templates use smarty-like syntax and translating goes as easy as writing this in the template:
    {{translate|This string should be translated}} And it defaults to the original string if no translation is given. This is nice because we can finish the entire project before thinking about translation, we get default english values printed out instead.
    All the actual translations reside in a database, but that can be handled by gettext if i wanted to, in the backend.

    The downside is that only hardcoded data can be translated this way so it doesnt cover every need for translation you might have. Apart from that im very happy with the solution we landed on, and the implementation, it works _great_ and made language support über easy.

    Just a suggestion, i dont know how you want to put the concern in your MVC implementation tho...
    Raymond Julin
    Developer: Hardware.no, Amobil.no, Gamer.no, Prisguide.no ...
    Owner: Kulturo.no

  7. #7
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi

    Quote Originally Posted by Findus
    All the actual translations reside in a database, but that can be handled by gettext if i wanted to, in the backend.
    Ok and where do you query your database to get the translation, so you can use it in your templates? In what part of your application? Anyway, I want to get rid of any template language as I find it to much overhead. I don't use any special feature of smarty anyway...

    Quote Originally Posted by Findus
    Just a suggestion, i dont know how you want to put the concern in your MVC implementation tho...
    Thanks. Yes, I don't know neither. My current solution works for me, but I know it's almost unsable for other projects due to it's 'inner dependency'

    I have some general view how it should be splitted, but THE glue... arghh...

    Daniel

  8. #8
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm...

    > Ok and where do you query your database to get the translation, so you can use it in
    > your templates?

    Here is an example of how I do it at the moment, but it's going to change soon...

    PHP Code:
    // some controller
    $page = new WebPage$nodes$request $context -> get'irequest' ) );
    $page -> attach( new Model( ... ) );
    $page -> attach( new OtherModel( ... ) );
    $page -> render'path/to/template/template.tpl' );
    // ... 
    And then, in the WebPage::render( $template); class method you iterate over the previously attached Model(s), ie

    PHP Code:
    // ...
    foreach( $this -> observers as $observer ) {
    $observer -> push$this );
    }
    include_once( 
    $template );
    // ... 
    The Model in question therefore, dumps the data in question to the View for you

    EDIT:

    > Although it would be great to see some more code...

    Sure, your Model class would implement the IAcceptee Interface, such as

    PHP Code:
    interface IAcceptee {
    public function 
    push$acceptable );

    And your Model would put the data pulled from the datasource to the View via this,

    PHP Code:
    // some model
    public function push$acceptable ) {
    $acceptable -> import( new Parameters( array( 'stories' => $this -> queryStories$acceptable -> request() -> get'id' ) ) ) ) );
    // simple as that really ;)

    public function queryStories$id ) {
    // ...

    I have in the past posted to this forum, the Parameters class, et al if you care to look for it - I don't have it at hand at the moment
    Last edited by Dr Livingston; Aug 28, 2006 at 08:38. Reason: ...

  9. #9
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi doc

    Hmmm.. That's pretty interesting.
    But it's not so cool if you're going to change it soon, is it ?

    Still, it looks a bit what I'm looking for. Although it would be great to see some more code...

    Thanks!

  10. #10
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Confusing typo

    Using a template engine, such as smarty, complicates matters a bit. Since you have to push any data to the view, you will need a third component between the controller and the template, whose purpose is to load data into the template. Conceptually, this component becomes the "view", where the template is just a passive resource, utilized by said view. From there, the same division of labour proceeds.
    Personally, I never use this setup - I don't need the distinction between template and view. The only benefit I can see in this, is that the templates could come from an untrusted source. For certain kinds of systems, this feature could be required.

    Quote Originally Posted by dan7
    Actually, my presentation is separated from the logic. I have no business logic in presentation layer.
    OK - That's a good start. From your initial post, I couldn't tell that.

    Now looking at your example, this is how I would implement the same thing. As I already noted above, I use simple php-files as view, rather than a template-engine :

    main.tpl.php
    PHP Code:
    <?php include("header.tpl.php");?>

    <h1><?php echo $this->__('header'); ?></h1>

    <?php include("footer.tpl.php");?>
    page.php
    PHP Code:
    class Page
    {
        protected 
    $translator;

        function 
    __construct($translator) {
            
    $this->translator $translator;
        }

        function 
    execute() {
            
    ob_start();
            include(
    'main.tpl.php');
            return 
    ob_get_clean();
        }

        function 
    __($phrase) {
            return 
    $this->translator->getPhrase($phrase);
        }

    index.php
    PHP Code:
    require_once('page.php');
    require_once(
    'translator.php');
    $page = new Page(new Translator(new Database("localhost""root""secret")));
    echo 
    $page->execute(); 
    PHP Code:
    class Translator
    {
        protected 
    $db;
        protected 
    $lang 'en';
        protected 
    $stringtable = Array();
        
        function 
    __construct($db) {
            
    $this->db $db;
        }

        function 
    getPhrase($phrase$group) {
            
    $this->load($group);
            return isset(
    $this->stringtable[$this->lang][$group][$phrase]) ? $this->stringtable[$this->lang][$group][$phrase] : $phrase;
        }

        protected function 
    load($group) {
            if (!isset(
    $this->stringtable[$this->lang])) {
                
    $this->stringtable[$this->lang] = Array();
            }
            if (!isset(
    $this->stringtable[$this->lang][$group])) {
                
    $sql 'select word_'.$this->lang.' from translation_view where ident = '.$group;
                
    $this->stringtable[$this->lang][$group] = $this->db->queryAll($sql);
            }
        }

    I'm not entirely sure why you use the 'group' on your translation-table. Unless you have really many strings, I think the overhead with just throwing them in one big bowl is insignificant.
    Last edited by kyberfabrikken; Aug 23, 2006 at 00:47.

  11. #11
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, thanks for the reply!

    Quote Originally Posted by kyberfabrikken
    Personally, I never use this setup - I don't need the distinction between template and view. The only benefit I can see in this, is that the templates could come from an untrusted source. For certain kinds of systems, this feature could be required.
    I agree and as I said before, I'm gonna drop Smarty in favour of normal, php templates. I don't see any benefits of using Smarty right now, but don't have the time to convert everything to 'non-Smarty' and in the same time having corrupted deisgn anyway. I'll change that when I start a new branch...

    Quote Originally Posted by kyberfabrikken
    I'm not entirely sure why you use the 'group' on your translation-table. Unless you have really many strings, I think the overhead with just throwing them in one big bowl is insignificant.
    I don't know how many rows would that table consist of. Now it has over 100, but there are only 2 web pages (and only 1 complete), so I assume there might be around 600-800 rows when it's finished. I'm gonna use some caching with it anyway, because translations won't change too often..

    But it's just a fraction of the problem...

    I can see now, how I could build a generic page, that has only standard stuff with translations. Thanks for the tips, really.

    Now, I need to find a way, how to glue that generic Page with additional stuff, that is components like Login or Search and the content (depends on the actual page, it may be a registration form or for example list of newly added items etc). All the pages share the same 'main' template (along with components like Login etc) and their views just differs by the actual content

    Now, I have Login stuff buried inside the Page class and executed in Page's constructor. It also contains some ajax stuff, so when there's an error and the request was made through XMLHTTP, then the json string with error is sent to the browser instead of rendering the whole page with an error message in the Login form.

    I know it's a bad design, but I can't find a way to make it a separate component and just 'attach' it to the Page and make the rendered form show in the main template. I know only the dirty way to accomplish that

    Same goes to any other component. Having this one solved (assuming it's just one of the many components on the given page) would help me to make the next important step...

    Thanks to everyone for help. I really appreciate it!

  12. #12
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    I don't know how many rows would that table consist of. Now it has over 100, but there are only 2 web pages (and only 1 complete), so I assume there might be around 600-800 rows when it's finished. I'm gonna use some caching with it anyway, because translations won't change too often..

    But it's just a fraction of the problem...
    Yeah - That's really a performance issue. For any project, I have worked with so far, I have simply used a php include-file with a translation-table. I don't split it further into groups - a few hundrede variables isn't much of a problem. I would reckon that it depends on the size of the application though.

    Quote Originally Posted by dan7
    Now, I need to find a way, how to glue that generic Page with additional stuff, that is components like Login or Search and the content (depends on the actual page, it may be a registration form or for example list of newly added items etc). All the pages share the same 'main' template (along with components like Login etc) and their views just differs by the actual content
    The way I percieve it, thoose are separate components, that need to be composed into one larger. You can do this in two ways (or a combination of). Either you assemble at the view-level. This is basically about putting an include("othertemplate.tpl.php); in the main template. This has limited use, but is fairly simple.
    If your components exhibits behaviour of any sorts, then you will have to go up one layer, and assemble at the controller-level. Expanding on the working example, let's add a login-box to our page :

    loginbox.php
    PHP Code:
    class LoginBox
    {
        protected 
    $translator;

        function 
    __construct($translator) {
            
    $this->translator $translator;
        }

        function 
    execute() {
            if (
    $_SERVER['REQUEST_METHOD'] == 'post' && @$_GET['action'] == 'loginbox') {
                return 
    $this->POST();
            }
            return 
    $this->GET();
        }

        function 
    POST() {
            
    // handle login here ...
        
    }

        function 
    GET() {
            
    ob_start();
            include(
    'loginbox.tpl.php');
            return 
    ob_get_clean();
        }

        function 
    __($phrase) {
            return 
    $this->translator->getPhrase($phrase);
        }

        function 
    url() {
          return 
    "?action=loginbox";
        }

    loginbox.tpl.php
    PHP Code:
    <form method="post" action="<?php echo $this->url(); ?>">
    username: <input type="text" name="username" />
    <br />
    password: <input type="password" name="password" />
    <br />
    <input type="submit" />
    </form>
    And some modifications to our code from before :

    page.php
    PHP Code:
    class Page
    {
        (...)
        
        protected function 
    getLoginBox() {
            
    $childcontroller = new LoginBox($this->translator);
            return 
    $childcontroller->execute();
        }

    main.tpl.php
    PHP Code:
    <?php include("header.tpl.php");?>

    <h1><?php echo $this->__('header'); ?></h1>

    <?php echo $this->getLoginBox(); ?>

    <?php include("footer.tpl.php");?>
    The url() method is kind of an approximation, since that would depend alot on how your routing-mechanism works. That's likely going to be your next question, but for now I'm just pretending that you actually have a working way of routing requests to controllers, since that's a whole problem on it's own.

  13. #13
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi!

    Well, it looks great and I'm starting to see some serious light..

    Quote Originally Posted by kyberfabrikken

    PHP Code:
    class Page
    {
        (...)
        
     
        protected function 
    getLoginBox() {
            
    $childcontroller = new LoginBox($this->translator);
            return 
    $childcontroller->execute();
        }

    So that's besically ALL that should be included in the main controller regarding the Login stuff, right?

    Each of my form has some unique field that identifies it, so I'm able to find out which form was submitted by examining the $_POST array. I'm actually using some pear package for form handling, but I'll probably drop it too.

    Ok, so now I kow how to build a login box and how to put it into ANY template along with sharing the translation object (which will either query the database or flat file..)

    Now I have a bunch of other questions
    Quote Originally Posted by kyberfabrikken
    PHP Code:
    require_once('page.php');
    require_once(
    'translator.php');
    $page = new Page(new Translator(new Database("localhost""root""secret")));
    echo 
    $page->execute(); 
    That was the code for index.php

    Now, it was OK when translator needs the DB object.
    But what, if other components need that object as well?
    Login needs it for validation for example..

    Should I use something like
    PHP Code:
    require_once('page.php');
    require_once(
    'translator.php');

    $db = new Database("localhost""root""secret");

    $page = new Page($db, new Translator($db));
    echo 
    $page->execute(); 
    And then, share the $db object between the components that need it by for example:
    PHP Code:
    class Page
    {
       function 
    __construct($db$translator) {
            
    $this->translator $translator;     
    $this->db $db;
        } 
        
        protected function 
    getLoginBox() {
            
    $childcontroller = new LoginBox($this->db$this->translator);
            return 
    $childcontroller->execute();
        }

    Is this the way to go?
    What if I need a Cache object in multiple components. Should I use it the same way as the $db one above? Is that 'the correct' way?
    While DB would be probably used on each page, Cache should be rather optional. I can imagine a page, where it won't be needed.
    I can also imagine some other stuff shared between components..

    Now, let's assume there is a registration page which has some form handling stuff. I can code this component the same as login box, but I need the handling of that in the controller.

    So what would be the correct way to do it?
    I can extend the Page controller and make something like (I'm still php4 guy, so forgive me any errors)
    register_c.php
    PHP Code:
    class Register_Page extends Page
    {
       function 
    __construct($db$translator) {
            
    $this->translator $translator;     
    $this->db $db;
        } 
        
        protected function 
    getRegistrationBox() {
            
    $childcontroller = new RegistrationBox($this->db$this->translator);
            return 
    $childcontroller->execute();
        }

    and then...
    register.php
    PHP Code:
    require_once('register_c.php');
    require_once(
    'translator.php');
    $page = new Register_Page(new Translator(new Database("localhost""root""secret")));
    echo 
    $page->execute(); 
    Is that the way to go? Should I stick to such a schema?

    The last small questions regarding LoginBox
    There are a few things that bugs me here.

    1. I need a validation. Should it run in the login controller or in some Model for the Login? Login data is actually part of the User Table in the database. User data will be managed in the admin panel, or after user logs in, he can change his data etc. Should I make 1 model 'User' and share it between all those components?

    For example, the User model could have a method like validateLogin($login,$password) which would return true/false

    Or should I write a separate Model for Login? (If Model at all)

    Each field that is to be validated has some different error messages (in many languages), so if I use validateLogin or validateWhateverForm in the Model, then I would also have to pass the translator object, so I can bind each validation warning to the correct message in correct language

    After user logs in, some field in the database should be updated (like 'last login date'), so some method which alters the DB should be invoked 'somewhere'

    2. There is one problem with the current design. Language is specified in the Translator but I feel that's not correct. Language should be system wide, because it affects the Models too.
    We can imagine 'News' section'. While it has some static stuff to be translated, the Model should fetch the correct fields from the database depending on the global language specification.
    So when the language is 'en' it should fetch for example comment_en field from the DB table

    Now, how to achieve the desired results with the current implementation? I can imagine some 'guessLang' method in the main controller, which will check a few things to determine the language of the app ($_GET, $_SESSION, http headers)

    I know, that's A LOT of questions.

    I just need those things solved once and for all...
    I just can't wait to refactor the whole project. I'll probably start this week, because you guys have provided me with TONS of great ideas.

    THANks!!

  14. #14
    SitePoint Zealot krt's Avatar
    Join Date
    Sep 2005
    Location
    Australia
    Posts
    114
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Won't reply to everything but a few quickies:
    - the LoginPage controller is fine
    - passing commonly used objects (eg: a database connection and a translator object in your case) is tedious, especially when you have a fair amount or when you later add a new one. I prefer using a registry (objects are stored in an array inside a utility class for example and accessed via the registry, each object with a unique identifier. So you firstly register the db object with an identifier, and then whenever you need it, simply use:
    PHP Code:
    $this->db Utility::registry('db'); 
    - Validation should be in the controller (may be a little to do with personal preference)
    - Language should be identified in the front controller based on GPC and session data and a language code could be set, either as a constant in a common base class or a global defined constant
    - To access content based on language, the model should get the appropiate field and revert to available language if not found

  15. #15
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the tips!

    Quote Originally Posted by krt
    - passing commonly used objects (eg: a database connection and a translator object in your case) is tedious, especially when you have a fair amount or when you later add a new one. I prefer using a registry (objects are stored in an array inside a utility class for example and accessed via the registry, each object with a unique identifier. So you firstly register the db object with an identifier, and then whenever you need it, simply use:
    PHP Code:
    $this->db Utility::registry('db'); 
    Oh yeah, I remember reading about it.
    So, I should register the needed objects in my index.php using some Registry class and then just use them when needed (from within any controller or model), right?

    Quote Originally Posted by krt
    - Validation should be in the controller (may be a little to do with personal preference)
    Yes. I've encountered different approaches to it. Some put it in the model, some in the controller. I haven't decided yet where I'd prefer to see it and thus my questions...

    Quote Originally Posted by krt
    - Language should be identified in the front controller based on GPC and session data and a language code could be set, either as a constant in a common base class or a global defined constant
    Oh yeah. I forgot about that option - that is, a global defined constant
    It should be much easier to determine the language in the front conroller and set the global constant with the value (that should be done in the constructor, isn't it?). Language won't change across the components... Thanks!
    Quote Originally Posted by krt
    - To access content based on language, the model should get the appropiate field and revert to available language if not found
    The front controller will always set the language. If it won't be able to determine any valid language sesison etc, it will just set the default one. So any model, that uses the language to determine what content to fetch will always have access to the VALID value (set by front controller). I hope I'm not mistaken

    Thanks for joining the discussion!

  16. #16
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by krt
    - Validation should be in the controller (may be a little to do with personal preference)
    I was thinking about it. I'd rather opt for validation in model. Validation needs to query the database many times and thus 'talk' with the data. That should be done in the model, isn't it? Examples would be: checking login/password in Login stuff or checking if the name isn't already taken in Registration stuff

    I kinda agree with Tony Marston solution in this regard (in general):
    The Model-View-Controller (MVC) Design Pattern for PHP

    I'm interested in other opinions though

  17. #17
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Tony Marston is not an authoritative source on anything except Tony Marston. In particular he doesn't have a clue about MVC.

  18. #18
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Validation is a cross-cutting concern. It may be done entirely in the controller, but if you need access to persistent data, you're moving into the models domain. I tend to think about validation as a controller-task, which may involve using the model. If the business-rules are complex enough, I'll make validation-functions in my model-components, which the controller calls.

    Deciding the users current language is a task for the frontcontroller alright, but I would't recommend using a global for storing the information. Pass it in to the lower level controllers instead.

    Passing in objects and sharing commonly used objects (like the db connection) is a problem on it's own. Highly decoupled code tends to depend on a lot of objects to get things done. This is not a sign of bad code, rather the opposite. There are several ways of solving this puzzle.

    The simplest soltution is to bite the apple, and simply pass things in. In my experience, people focus a lot on the issue, even though it's not really that problematic. After all, you it's mostly a few general objects, that needs many objects passed in, and only a creation-time. Even though the code may look a bit unwieldy, atleast it's very explicit.

    If you don't want that, you could use global variables of one kind or another. I'm only mentioning this for sake of completeness - Global variables are very bad design, and should be avoided like the plague. This includes variants of the theme, such as singleton, static classes and whatnot. Constants are a slightly less dangerous variant, mostly because of its limited use. I'm not going into a deeper explanation of my stance at this point, but google for globals + evil if you want.

    A better solution is a registry. Basically, a registry is a collection of objects, which you pass around. In PHP, you can simply use an array for the purpose.
    The main benefit is, that you only have to pass one object, rather than multiple. Still it's not ideal.
    Another benefit of a registry is, that it can be expanded with a factory to facilitate lazy-loading. Such a construction is sometimes called a service locator, but the words are used a bit differently depending on who's talking. This is the design I use myself 99% of the time.
    Curiously, the example krt shows us above, isn't really what I understand as a registry - it's simply a global in disguise.

    If you're still not satisfied, you can bring in the big guns. Dependency injection containers are clever factories, that are able to inspect classes and through reflection determine its dependencies and resolve it behind the scenes. Since most implementations relies on interfaces for identifying dependencies, you won't get far with PHP4 though. Note that it's not a fundamental show-stopper. You could implement a DI container in PHP4 if you really want.

    This is all just a short resume on the topic. It has been disgussed a lot at this forum recently, so do some search for the keywords for more reading.

  19. #19
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    Validation is a cross-cutting concern. It may be done entirely in the controller, but if you need access to persistent data, you're moving into the models domain. I tend to think about validation as a controller-task, which may involve using the model. If the business-rules are complex enough, I'll make validation-functions in my model-components, which the controller calls.
    So you never call any validation rules in the model and if data access is needed you just call some method from the corresponding model?
    But even if you access the data, you actually make a validation of this data in your controller, right?

    Quote Originally Posted by kyberfabrikken
    Deciding the users current language is a task for the frontcontroller alright, but I would't recommend using a global for storing the information. Pass it in to the lower level controllers instead.
    I understand that, but what's exactly wrong to have the lang declared as a constant? I wouldn't have to remember to pass it here and there.. I would just use it where I need it too... Or there's a catch?

    Quote Originally Posted by kyberfabrikken
    I'm not going into a deeper explanation of my stance at this point, but google for globals + evil if you want.
    Yeah, I know... I haven't used any globals for quite a long time..

    Quote Originally Posted by kyberfabrikken
    A better solution is a registry. Basically, a registry is a collection of objects, which you pass around. In PHP, you can simply use an array for the purpose.
    The main benefit is, that you only have to pass one object, rather than multiple. Still it's not ideal.
    Maybe not ideal, but good enough... I'll read other posts about this solution

    And what do you think about one of my previous questions about Login model. Should I make a separate model or use other model that describe the same data (for example 'User'). Because there are different components using the same model, the model class would be a bit bigger if not separated...

    Thanks!

  20. #20
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    314
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    Hi

    This is my first post here. I'm currently working on a bit complex website/application and I've came to the point, where it's getting harder to maintain the code.
    And it will get even more harder when you plan on extending your own class. You see, you are wasting a lot of time trying to learn something that's already been done a million times before. In fact, Smarty, Zend Framework API to name a few have already did just this for you to use.

    There is no point in reinventing it over again. Review what's available and extend as you need to. That is the point to open source availability.

    You could already have a working framework, you could have already added in your own code to the existing frameworks out there and you could right this moment be working on your ultimate goal of creating your app the way you see it.

    Just a thought.

    Good luck

  21. #21
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    So you never call any validation rules in the model and if data access is needed you just call some method from the corresponding model?
    But even if you access the data, you actually make a validation of this data in your controller, right?
    Not sure what you're asking here, but ... The model assumes that whomever called it has full privileges, so it would only object if the request is illogical. It's the controllers task to assure that the user has the appropriate privileges. It may well use a function on the model to verify this, though.

    Quote Originally Posted by dan7
    I understand that, but what's exactly wrong to have the lang declared as a constant? I wouldn't have to remember to pass it here and there.. I would
    just use it where I need it too... Or there's a catch?
    It's a global. Globals are one big catch.
    A global variable always has implication for the full scope of the process. This means that any other code that runs in the process is affected. Theese dependencies are potential errors.
    Declaring the language as a constant is kind of restricting yourself quite a lot. What if you wanted to render the loginbox in one language, and the main content in another. This may be a rather artificial example, since it's probably not a requirement for your current application, but what if your requirements change?

    Quote Originally Posted by dan7
    And what do you think about one of my previous questions about Login model. Should I make a separate model or use other model that describe the same data (for example 'User'). Because there are different components using the same model, the model class would be a bit bigger if not separated...
    I split the login-action into two steps. One is about gathering the credentials. Theese could come from some source such as $_SERVER['PHP_AUTH_USER'] + $_SERVER['PHP_AUTH_PW'] or $_SESSION['username'] + $_SESSION['password']. Once I have the credentials, I fetch the user from a model-component (a user-gateway), and store this in the registry.
    This is all implemented as an action in the front controller, so it happens on each request.
    What the login-form does then, is simply updating the $_SESSION with the posted credentials.

  22. #22
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by psalzmann
    There is no point in reinventing it over again. Review what's available and extend as you need to. That is the point to open source availability.

    You could already have a working framework, you could have already added in your own code to the existing frameworks out there and you could right this moment be working on your ultimate goal of creating your app the way you see it.
    Yes, but I don't ask how to write a Zend Framwork killer. I want to learn a better way to write my code. This is the priority and the main point of starting this thread.

    I'm not saying frameworks are bad etc. I just don't want to use any. Not at this moment anyway..

    I'm in the middle of the work on my project (the old way) and there is no time to switch to some framework nor to refactor etc. I'd rather start a new branch from the repository and slowly convert the code in my spare time using the given examples (I will probably ask more questions as the work progresses - sorry!)

  23. #23
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    Not sure what you're asking here, but ... The model assumes that whomever called it has full privileges, so it would only object if the request is illogical. It's the controllers task to assure that the user has the appropriate privileges. It may well use a function on the model to verify this, though.
    Yes, sorry. Let me ask you by example. User is using register form to make a new account so during validation, you need to check if user with the selected name already exists.
    You either call in your controller $model->userExists($username) which returns true/false
    or you call $model->getUser($username) and determine if the result is true/false in your controller (instead of model).
    The 2nd method example is only fetching and returning the data to the controller

    Quote Originally Posted by kyberfabrikken
    Declaring the language as a constant is kind of restricting yourself quite a lot. What if you wanted to render the loginbox in one language, and the main content in another. This may be a rather artificial example, since it's probably not a requirement for your current application, but what if your requirements change?
    Well, maybe you're right.. I actually may need to switch languages between components ... Ok, I'll forget about this global for now.. But now I would have to re-think how index.php looks like.
    Translation object needs to know the language, so I either create the object in Page contructor (I will know the language there), or I'll do something like
    PHP Code:
    class Page
    {
        protected 
    $translator;

        function 
    __construct($translator) {
            
    //language stuff invoked here, $this->lang is set / changed

            
    $this->translator $translator;
            
    $this->translator->setLanguage($this->lang);

        } 

    or

    PHP Code:
        (...)
        function 
    __($phrase) {
            return 
    $this->translator->getPhrase($phrase,$this->lang);
        } 

    Quote Originally Posted by kyberfabrikken
    This is all implemented as an action in the front controller, so it happens on each request.
    What the login-form does then, is simply updating the $_SESSION with the posted credentials.
    Ok, that's clear. I will need to think about it more. In my admin panel I have a global structure of the menu in an array. Global means it contains all possible entries in one array structure. After user logs in and accesses the admin area, I'm checking his priviliges and iterate over the menu structure removing the entries user has no privileges to. I call it in the constructor, after extracting information from the _SESSION. Now I'm wondering if that's a correct place to do it... The menu is a presentation only - I mean the structure I described doesn't determine where user has access to. It rather determines what user sees he has access to

  24. #24
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by dan7
    Yes, sorry. Let me ask you by example. User is using register form to make a new account so during validation, you need to check if user with the selected name already exists.
    You either call in your controller $model->userExists($username) which returns true/false
    or you call $model->getUser($username) and determine if the result is true/false in your controller (instead of model).
    The 2nd method example is only fetching and returning the data to the controller
    I think either way is acceptable, although the first is probably most correct. If I'm in doubt, I put the logic in the controller. Later, if I realize that I'm duplicating code across multiple controllers, I refactor and move it into the model. You don't necessarily need to get it 100% right the first time around.

    PHP Code:
        (...)
        function 
    __($phrase) {
            return 
    $this->translator->getPhrase($phrase,$this->lang);
        } 
    I prefer that method over setting a state on the translator object.

    Ok, that's clear. I will need to think about it more. In my admin panel I have a global structure of the menu in an array. Global means it contains all possible entries in one array structure. After user logs in and accesses the admin area, I'm checking his priviliges and iterate over the menu structure removing the entries user has no privileges to. I call it in the constructor, after extracting information from the _SESSION. Now I'm wondering if that's a correct place to do it... The menu is a presentation only - I mean the structure I described doesn't determine where user has access to. It rather determines what user sees he has access to
    I think I would implement the menu as a distinct controller, and then include it just like in the loginbox example above.

    While we're at the issue of globals, remember that $_GET, $_SESSION etc. are globals too. I never use them directly inside controllers, but rather put them in the registry, and pull it from there. (To be honest, that's an oversimplification, but in concept ...).

  25. #25
    SitePoint Zealot
    Join Date
    Aug 2006
    Location
    Poland
    Posts
    108
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    I think I would implement the menu as a distinct controller, and then include it just like in the loginbox example above.

    While we're at the issue of globals, remember that $_GET, $_SESSION etc. are globals too. I never use them directly inside controllers, but rather put them in the registry, and pull it from there. (To be honest, that's an oversimplification, but in concept ...).
    Thanks for your patience and a lot of responses. OK, distinct controller sounds good. But this way, the components list will grow bigger and bigger. I'm wondering if I should still create all component objects in index.php (or whatever script user requests) and pass ALL of them to the front controller like you suggested before.

    When we consider using XMLHTTP, creating all the objects on every request may be a little overhead (For example, there's no need to build menus or some other stuff, when user just called some action through XMLHTTPRequest object and he only expects to receive some JSON string with response.). I'm wondering how to deal with that. Well, I can forget about it for now of course and treat it like a regular request (with the different 'view' I suppose) until I actually finish the 'standard' layer of the application

    About $_GET etc globals - yes I know they are globals too I'v already read several threads here with mixed opinions about working with those variables in MVC pattern

    Thanks!


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
  •