A most basic example, I have a class that I use for fetching data for one of information web pages:
class InfoPage {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function getPage($slug) {
return $this->db->fetchAssoc("SELECT title, description, content
FROM pages
WHERE id=?",
[$slug]);
}
}
In this example I get data of the page in an array form. I don’t have an ORM nor an entity class. I just have a specific class that deals with fetching data for a web page. This is very simple but of course I can make it much more elaborate, if required, I use complex SQL with joins, functions, etc. depending on my needs.
As I mentioned earlier, object notation was only an example, I could do the same thing using arrays and then the nested loop would be identical except for array syntax.
As I see it there are 3 major forms of fetching data by data access objects:
- As array.
- As dumb object (of type stdClass).
- As specific objects, i.e. entities.
In many cases there is not much difference between 1 and 2 except for different syntax. In some cases I even prefer arrays because I can use a whole set of PHP array functions on them while not on objects. Arrays are really fine for dumb data structures.
Form 3 is also a good choice but you have to write your entity class, of course. This is more work but the bonus is that your entity class can be documented well and you can give it a ‘signature’ by specifying all fields (properties) and perhaps getters and setters. Then when you pass an entity object like this in your code you know what kind of data it encapsulates, what fields are available, you get IDE hints with your PhpDoc blocks, etc. Whereas when passing arrays or stdClass objects you don’t know what kind of data they hold, which can make the code less clear.
To be honest, I often use arrays because it takes less effort - but I’m aware that in case of debugging I need to carefully track down where the data comes from and what’s in there. This is a trade-off. I use entities mostly in cases where I write a module (a class, or a collection of classes) that I expect to be separate and to (be able to) re-use it later on and I want it to have a well defined interface. When you see that a class method requires a PageInfo entity object then you can easily know what data it needs by looking at PageInfo class, whereas when an array is required then you don’t know what the array is supposed to hold without looking into the module’s code (or without reading the documentation, if there is any). The same applies to data returned by the class methods.
When a collection of data is returned then I return an array of one of the same data types:
- an array of arrays
- an array of stdClass objects
- an array of entity objects
You can also have a variation of the array by returning an object implementing ArrayIterator - but we don’t have to go into such details here. Such an object will behave almost like an array.
If there are nested structures like in your example then there will be multidimensional array of arrays or objects.
The difference between your method and mine is that you are using generic methods like findById()
that are used for all your tables/entities. I’ve found this works well for simple CRUD applications because you can cover almost everything by using those generic methods. When the requirements increase those methods often will not be enough and we will have to code around them.
For example, imagine you are using findById()
to fetch page data for display. But one day you want to fetch just one more value - number of comments for that page and the most efficient way is to use a subquery in your SQL. I just add a subquery in half a minute and I’m done while you have to completely change your approach because findById()
can’t do it for you. This may not sound like an ORM solution would be very hard to find just to fetch comment count - I’m aware there are ways to do it - but there may be a hundred other non-CRUD-standard requirements and in some cases this may become tedious.
And BTW, I don’t mean to say which way is better, I just want to present a different take on the matter. Some people simply enjoy as much OOP as possible and enjoy abstracting things like database access, etc. while others feel more comfortable with SQL, and of course depending on project type one approach may be more reasonable than the other. But personally, one day I decided to go ORM-less for my new projects and I really can’t look back - I just feel relieved 