We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

"Dressing up" my data

My first Phalcon project is a rebuild of an old procedural database interface, and sometimes I have trouble knowing where in the MVC structure some given functionality should go. I'm currently trying to stick to using Volt for templating, partly because I would otherwise be tempted to do too much coding in the Views.

My current dilemma is special treatment of data fields for display. For example, my Birthdate field is a MySQL date field and therefore must include year, month, and day, but sometimes the year is not known. I use 1900 as my hidden clue that this should be treated as just a birthday with an unknown year, but at what point on the data's travel from Model through Controller to View should I replace the "1900"? I also need to display the age, which is a calculation that is definitely not posssible in Volt even if I was tempted to do it there.

The original suggestion given to me was to add a method called afterFetch() to the Model to trap that event, and do my calculations and put the results in additional properties to $this (each record as it is fetched), which I have been nicknaming "pseudo-fields", because they aren't in the database but sit in the Model as if they are. But I have realized a couple of problems with that:

  1. The only time that method runs is when my query is a straight SELECT * FROM thetable ... - if I get just a few fields from the database, the pseudo-fields don't appear, and although I haven't tested a join yet, I bet it doesn't work in that case either.
  2. I haven't started writing the code for editing data yet, but I suspect that the save() method won't be happy if there are extra fields in the Model that aren't in the database.

So then I consider the Controller, but since it merely passes the array of objects along, I would have to loop through it just for this purpose. Finally, I get back to thinking of the View, because it seems to me the sort of thing that is most closely related to display of the data. But it has too much business logic for Volt.

What do you guys do with this kind of data handling?

i would do that in model of application with method getYear or getBirthdate and i would do all checks inside that method (if year === 1900 etc.)



10.9k
edited May '14

I would encourage making the afterFetch the place to start and making a variable for a view date and leave Birthdate alone.

Something like:

public function afterFetch()
    {
          // Check that birthdate is available and is set to the year 1900
          if(isset($this->birthdate) && !empty($this->birthdate) && date('Y', strtotime($this->birthdate)) == 1900) { 

                // It is 1900. Set to a day/month format
                $this->birthdate_for_view = date('d/m', strtotime($this->birthdate)); 
          } else {

                // Set to a day/month/year format
                $this->birthdate_for_view = date('d/m/Y', strtotime($this->birthdate));
          }

    }

The above presumes the birthdate from the database is like so: 1900-12-25 or 1900-12-25 00:00:00

@pixiesky: That's basically what I'm doing now. But if I don't query all fields in my table, {{ person.birthdate_for_view }} doesn't exist in the View, so I'm assuming that the event didn't fire or something. (I haven't gotten xdebug running yet, so I can't check that directly, but that's a different issue.) I'm not far along in my learning MVC to know how I'm supposed to handle situations where I need fields from multiple tables (a join) - whether I need a special model for that or if Phalcon will use the models for each table. I picked one of the simplest pages in the application as a starting point; most of the rest will need joins, concatenates, etc. When I get to that, I suppose I'll find out whether afterFetch fires in that case or not, but I bet I'll have the same problem.

And although the page I'm working on now is only for display, this will get added every time the model gets data, so for pages that do CRUD I'm concerned that save() will get confused by having extra stuff in the model that doesn't match the schema of the database.



10.9k

Venturing in to MVC in the early stages does kind of melt your head a bit but it's worth it.

Yeah, now that you mention it, I did set up some relationships a while ago, but then forgot about them after it didn't seem likely that I would use them very much. Using relationships and PHQL saves a few "ON" clauses in joins over simply writing raw SQL, but its drawbacks are far more significant for someone like me who is used to writing my own SQL anyway: it won't allow subqueries, GROUP_CONCAT(), and probably several other MySQL-specific techniques, and it does two extra queries before every data retrieval (a check to make sure the table exists and an EXPLAIN statement), adding to the MySQL engine overload significantly.

After spending a couple hours trying to figure out how to reference data from a join (if my table name is "person", my model must be "Person", and the join must use the model name, but the additional object layer added because of the join is named after the table, not the model - https://docs.phalcon.io/en/latest/reference/phql.html#creating-queries-using-the-query-builder didn't explain that), I finally got a little test running. It appears that my pseudo-fields do work as long as I ask for Person.*,Household.*. But if I try to ask for individual columns, the whole thing falls apart again.

Yes, brain melt is in full swing!



10.9k

Yeah you have to stick to naming conventions for stuff to work in the code but an odd table name can be used by declaring in your model what the table is.

    class Robots extends \Phalcon\Mvc\Model
    {

        public function getSource()
        {
            return "the_robots";
        }

    }

But of course then things get start to get more confusing in the long run.

https://vimeo.com/63022489 << If you havn't seen that one yet then do. It demonstrates building a model file with phalcon tools and is worth knowing and seeing what the 'default' model file is. Gives you a feel for the place as well.

It sounds to me that PHQL is the way to go in your case. One the things about using a framework's system of talking to the database is that you can switch to a different database easily in the future in the case, say, MySQL can't handle the site anymore or a server move or whatever. You may also prefer to get arrays from your queries; in which case ->toArray() plonked on the end (technical term that) can be used.

I've never actually tried the following but I am presuming it works:

    $phql = "SELECT CONCAT(c.id, ' ', c.name) AS id_name FROM Cars AS c ORDER BY c.name";
    $cars = $manager->executeQuery($phql)->toArray();
    foreach ($cars as $car) {
        echo $car['id_name'], "\n";
    }

My point being once you have settled on how your model/s are going to function with the options available you can then do your best to keep data in the model. Models should have most of the code as well; controllers should be as slim as possible. If you need to manipulate the data then you should try to make that happen in the model; fat models thin controllers and views shouldn't have much logic at all unless it's for displaying something such as the date next to the copyright.