SitePoint Sponsor

User Tag List

Results 1 to 15 of 15
  1. #1
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Zend Framework - Helper class in need ? - Mega Doubt Post

    The issue:

    I have to list several elements that are coming from a database source A and they are:
    team_id, team_name and team_score (translated for explanation sake).

    I need to loop over them, and display that information.

    So, I have, on the DAO side:

    PHP Code:
        public function listOfTeams()
        {
          
    $select $this->select()
            ->
    from(array('t'=>'teams'), array('cod_team','name','score'));
          return 
    $this->fetchAll($select);
        
        } 
    On my team controller:

    Code PHP:
        public function listAction()
        {
          $teamsDao = new TeamsDao();
          $this->view->infoTeam = $teamsDao->listOfTeams();                    
        }

    And at the view:

    PHP Code:
        <?php for($i 0$i 30$i++): ?>
        
          <?php if(isset($this->infoTeam[$i])): ?>
    Now, the thing is, on each of those listed items, I need to add more information.

    That information doesn't come directly from a database, but it's a result of some calculations.

    Take for example the percentage of games done.

    Code PHP:
     $totalGamesNumber > 0 ? ($gamesDone*100)/$totalGamesNumber : 0);

    So, I need to grab the total games number from the database, then, for each team, I need to have the number of games done, so that I can have the percentage to be displayed.

    The $gamesDone are obtained by:

    Code PHP:
     $gameTeamDao->countGamesPerTeam($gameVo, $teamVo);



    The doubts:

    I'm stuck here, because I cannot see where/how should I call and make the method for calculating the percentage, in order to allow the percentage of games completed to be presented along with the other data.


    If you had to write an helper on this circumstances, how would you do it?
    I've heart about "helpers" but I don't understand how do they fit the case here.


    If your read it all, I want to thank you already, :s
    Márcio

  2. #2
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    In my opinion the calculations seem to be part of the domain model so the DAO is the best place for them.

    (pseudo code)
    PHP Code:
    public function listOfTeams() {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        
    $rows $this->fetchAll($select);

        foreach(
    $rows as &$row) {
            
    $row['percentage_of_games_done'] = $row['totalGamesNumber'] > ? ($row['gamesDone']*100)/$row['totalGamesNumber'] : 0);
        }
        
        return 
    $rows;


    There isn't anything wrong with adding the calculations after the data has been retrieved from the db. If anything this is one of major advantages of separating business logic from application logic. The model data may be derived from any data source, including calculations applied after the data has been fetched from the db.

    Alternatively though you could add a flag to mixin the data or place in a separate method. Though, I would only recommend doing this if the process to derive the data is resource intensive. Otherwise, using a consistent interface will avoid problems down the line.

    (pseudo code) - separate model methods
    PHP Code:
    public function listOfTeams() {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        return 
    $this->fetchAll($select);



    public function 
    listOfTeamWithExtraData() {

           
    $rows $this->listOfTeams();

        foreach(
    $rows as &$row) {
            
    $row['percentage_of_games_done'] = $row['totalGamesNumber'] > ? ($row['gamesDone']*100)/$row['totalGamesNumber'] : 0);
        }
        
        return 
    $rows;

    (pseudo code) - flag
    PHP Code:
    public function listOfTeams($mixin=false) {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        
    $rows $this->fetchAll($select);
         

            if(
    $mixin === true) {
             foreach(
    $rows as &$row) {
            
    $row['percentage_of_games_done'] = $row['totalGamesNumber'] > ? ($row['gamesDone']*100)/$row['totalGamesNumber'] : 0);
             }
            }
        
        return 
    $rows;


    Disclaimer: I'm not familiar with how CI returns rows, so the actual implementation may change slightly, though the concept will be consistent.

  3. #3
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    In my opinion the calculations seem to be part of the domain model so the DAO is the best place for them.

    (pseudo code)
    PHP Code:
    public function listOfTeams() {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        
    $rows $this->fetchAll($select);

        foreach(
    $rows as &$row) {
            
    $row['percentage_of_games_done'] = $row['totalGamesNumber'] > ? ($row['gamesDone']*100)/$row['totalGamesNumber'] : 0);
        }
        
        return 
    $rows;


    There isn't anything wrong with adding the calculations after the data has been retrieved from the db. If anything this is one of major advantages of separating business logic from application logic. The model data may be derived from any data source, including calculations applied after the data has been fetched from the db.
    Offtopic:
    I do agree with you. The only reason that I can see for doing those calculations on the controller instead of the model is because, that information (let's say: a number), can then be converted into: "a percentage" - "a base for some other calculations" etc... so we have like a raw number and, with that, a multitude of possible variations that each specific controller can manipulate.

    First, my last assumption could be wrong, since I do lack experience on this matters.
    Second, in this case however, and as far as I can see, that model calculation will serve only that percentage propose.


    Anyway:
    The thing is that
    $row['gamesDone'] is, in fact: $row['gamesDoneOnSpecificConditions']

    And that value, is a value that needs to be retrieved from a query COUNT with a lot of joins that, at the end will return the desired number.

    And my question is, having query A based on a given "teamId" and another query b that will return the "gamesDoneOnSpecificConditions" based on the same "teamId" - how can I put them together on a view, so that they I can properly list them as a group.

    Note: I'm on ZF, not CI.

    Thanks again,
    Márcio

  4. #4
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    Well you could either add the logic to the initial query to reduce the number of hits to the db or create separate methods that return that aggregate data, perhaps adding some type of cache for deriving the calculation – depending how frequently the calculation changes per team.

    (pseudo code)
    PHP Code:
    public function listOfTeams() {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        
    $rows $this->fetchAll($select);

        foreach(
    $rows as &$row) {
        
            
    $total $this->_getTeamsTotalGames($row['teamid']);
            
    $done $this->_getTeamsGamesDone($row['teamid']);
            
            
    $row['percentage_of_games_done'] = $total ? ($done*100)/$total 0);
        }
        
        return 
    $rows;



    private function 
    _getTeamsTotalGames($teamid) {
       
    /* ... */
    }

    private function 
    _getTeamsGamesDone($teamid) {
       
    /* ... */

    The return values of the two private methods could be cached in some form so that the aggregate query only needs to be issued once per request, session, etc – depending on how often they change.

    The other way is to modify the initial query your building in CI Query Builder to provide the aggregate data all in a single query, with the initial data.

  5. #5
    SitePoint Zealot romance's Avatar
    Join Date
    Apr 2004
    Location
    UK
    Posts
    181
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You could do the same as what oddz mentioned and perform the calculations on the row array before passing to the view or use a view helper which would wrap access to a model method such as below

    PHP Code:

    //from the view

    <?=$this->percentageHelper($this->infoTeam[$i]);?>
    and the helper code

    PHP Code:
    class Zend_View_Helper_PercentageHelper{

        public function 
    percentageHelper($rowArray){
            
    $teamsDao = new TeamsDao();
            
    $number $teamsDao->doSomeCalcs();
            return 
    $number;
        }

    perhaps looking at caching the method call if its going to slow down your results page if called for every row.

    btw, you may want to look into zend_paginator rather than $i<30 etc loops as it handles that sort of stuff for you.

  6. #6
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    Well you could either add the logic to the initial query to reduce the number of hits to the db or create separate methods that return that aggregate data, perhaps adding some type of cache for deriving the calculation – depending how frequently the calculation changes per team.

    (pseudo code)
    PHP Code:
    public function listOfTeams() {

        
    $select $this->select()
        ->
    from(array('t'=>'teams'), array('cod_team','name','score'));

        
    $rows $this->fetchAll($select);

        foreach(
    $rows as &$row) {
        
            
    $total $this->_getTeamsTotalGames($row['teamid']);
            
    $done $this->_getTeamsGamesDone($row['teamid']);
            
            
    $row['percentage_of_games_done'] = $total ? ($done*100)/$total 0);
        }
        
        return 
    $rows;



    private function 
    _getTeamsTotalGames($teamid) {
       
    /* ... */
    }

    private function 
    _getTeamsGamesDone($teamid) {
       
    /* ... */

    The return values of the two private methods could be cached in some form so that the aggregate query only needs to be issued once per request, session, etc – depending on how often they change.
    I will give it a try.


    Quote Originally Posted by romance View Post
    You could do the same as what oddz mentioned and perform the calculations on the row array before passing to the view or use a view helper which would wrap access to a model method such as below

    PHP Code:

    //from the view

    <?=$this->percentageHelper($this->infoTeam[$i]);?>
    and the helper code

    PHP Code:
    class Zend_View_Helper_PercentageHelper{

        public function 
    percentageHelper($rowArray){
            
    $teamsDao = new TeamsDao();
            
    $number $teamsDao->doSomeCalcs();
            return 
    $number;
        }

    perhaps looking at caching the method call if its going to slow down your results page if called for every row.
    True. For all 30 records, I would have to do that calculation and, as you say, it could be very database intensive when retrieving the results.

    But that $this->percentageHelper($this->infoTeam[$i] absolutly was what I was looking for, ignoring the performance hit that you have just pointed out.

    Quote Originally Posted by romance View Post
    btw, you may want to look into zend_paginator rather than $i<30 etc loops as it handles that sort of stuff for you.
    In this case, I will paginate nothing. I have 30 records, and it's all I can get. But thanks for the addon.



    SO:
    I will give it a try on using private methods.


    Thanks a lot really,
    Márcio

  7. #7
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @Oddz or anyone else that could explain it:

    1)
    Why the & here:
    PHP Code:
    foreach($rows as &$row) { 
    ?

    Thanks.

  8. #8
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    That would be what I meant by the implementation may change. That is an implementation based on the rows being arrays, rather than objects that pass by reference by default in PHP5.

  9. #9
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    2)

    PHP Code:
    _getTeamsTotalGames($row['teamid']); 
    Is, in fact:
    PHP Code:
    getTotalGames(); 
    On GamesDao Class.

    Should I instantiate GamesDao or, should I call it statically ON listOfTeams() or, neither of those ?

  10. #10
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    Is the other one a method already available somewhere also?

    If so I might change my recommendation to either mixing it in at the controller or just calling it in the view. The most politically correct would be passing the model to the view and calling it from there. It depends though. I generally try to avoid model dependencies on other models, that can become out of hand quickly if not managed well imo. I tend to keep models as separate, isolated entities independent of all other models, generally, unless unavoidable.

    Just out of curiosity what are the queries to fetch the total games and games done?

  11. #11
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    Is the other one a method already available somewhere also?
    Do you mean, if I'm calling that method somewhere else?
    Not at present I believe. But it could probably be used somewhere else yes.

    Quote Originally Posted by oddz View Post
    If so I might change my recommendation to either mixing it in at the controller or just calling it in the view. The most politically correct would be passing the model to the view and calling it from there. It depends though. I generally try to avoid model dependencies on other models, that can become out of hand quickly if not managed well imo. I tend to keep models as separate, isolated entities independent of all other models, generally, unless unavoidable.
    I understand the reasons for that change of recommendation.

    BUT:
    I've almost finish your initial recomendation implementation here, so for now, I will stay with that.

    I don't understand the & meaning. But I must mark it for read later on.
    For now, I see that: If I remove the & sign, the $row with the percentage is NOT incremented.

    If I keep it, the $row IS incremented.


    Quote Originally Posted by oddz View Post
    Just out of curiosity what are the queries to fetch the total games and games done?
    If it's for helping me out, then, worry not, I believe I'm getting your example implementation (despite my last doubt);

    If it's because you are curious about something else, then, I must appologize for not translating this last one. it's 01:50 AM here and I have to finish this until tomorrow morning, or should I say, some hours.

    However, and if you wish, I can translate them for the sake of a better compreention, later tomorrow - it's the very least I can do.

    So, the total games is a very simple one:
    PHP Code:
    /**
    * @desc Counts the total number of games existing;
    * @return <string>
     */
     
    public function totalGames(){

         
    $select $this->select()->from(array("g"=>"games"),array('COUNT(*) AS totalGames'));

           
    $row $this->fetchRow($select);
                
            return 
    $row->totalGames;
      } 

    And the other one is:
    PHP Code:
     private function _contaDesafiosDistintosCompletos($codEquipa)
            {
                
    $select $this->getAdapter()->select();
                
    $select->from(array("e"=>"equipas"),array('COUNT(DISTINCT d.cod_desafio) AS numeroDesafiosCompletosSemRepeticao'));
                
    $select->join(array("de"=>"desafiosporequipa"),"e.cod_equipa = de.cod_equipaFk");
                
    $select->join(array("d"=>"desafios"),"d.cod_desafio = de.cod_desafioFk");
                
    $select->where("de.estado = 1");
                
    $select->where("e.cod_equipa = ".$codEquipa);

                
    $row $this->getAdapter()->fetchRow($select);

                
    //o $row é array e não objecto. Por influência do getAdapter?
                
    return $row['numeroDesafiosCompletosSemRepeticao'];
            } 

  12. #12
    SitePoint Wizard
    Join Date
    Feb 2009
    Posts
    1,006
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Done it.



    Your implementation suggestion worked like a charm.


    Cheers,
    Márcio


    Ps- If you are curious about anything else, or/and you wish me to translate that method, please, just let me know.

  13. #13
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    I got a little preoccupied with something else. Good to see you were able to translate the chicken scratch to something useful and get it working.

  14. #14
    <?php while(!sleep()){code();} G.Schuster's Avatar
    Join Date
    Mar 2007
    Location
    Germany
    Posts
    428
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    In my opinion the calculations seem to be part of the domain model so the DAO is the best place for them.
    Absolutely NO.
    The DAO (Data Access Object) is interchangeable - would you really like to re-implement the same calculations with every DAO?
    Calculations are Business Logic and thus should reside in the model itself.
    A very common approach is to use a mapper which asks the DAO for data, creates an instance of the model and passes that back, e.g. to the controller.
    I would stick with that as you implement the Business Logic (here: calculation of games per team) at exactly one point, no matter how many different DAOs (DB, XML, Flatfile, WebService etc.) you implement, all share the same model.
    And as the mappers harmonize the data (to fit the model), you are able to rely on your models and - that's one of the main points - the DAO really gets interchangeable.

  15. #15
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    4 Thread(s)
    A mapper isn't being used here, therefore that is not a practical solution. The DAO is still interchangeable. The only different is that each method has it own return value based on the business needs at hand. The only real difference between the solution oikram is using is that it lacks a concrete mapping entity. In my opinion that is a better approach because the entire system becomes more flexible and every method isn't bound to some mapper entity interface. Mappers have their place but oikram isn't using a mapper and there really isn't a need for one. Blindly following a mapper religion and enforcing it on others at every wimp is not something I believe in. Especially for a situation that doesn't really warrant it.


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
  •