Ah, so getters is just to define what I need? Then I set up whatever methods I need?
Yes. You just define what getters are available in the structure, and then those properties will pull their values from the corresponding method.
That's where I can see myself giving up! I'm just trying to wrap my head around the XF framework and how I can integrate it with some existing code from custom tables.
One of the biggest issues people have with ORMs (which the finder/entity system ultimately comprise) is the resulting
impedance mismatch. ORMs are very convenient for simple cases, but they can get in your way when you need to stray from the happy path.
On the whole, an entity is an object which corresponds to an entire row in a table. The properties of the object correspond to the columns of the row. You can use getters to derive data from the columns (or elsewhere). A finder is for selecting one or more entire rows from a table and using them to hydrate entity objects. Doing anything more (or less) than that can be challenging to map cleanly to this paradigm.
I know I can set up relations inside the Entity, so I'm assuming I can have multiple table relations as well?
You can use relations to build a sort of graph of entity objects, but again that comes with all the limitations inherent to those objects, and can require thinking about your data and designing your schema a bit differently.
When I have something like this:
In this case, the usual approach would likely be to denormalize the data by storing
total_points
and
entered
as columns on the parent table (
table1
), and updating their values when data is changed on the child table (
table2
).
An alternative approach might be to have
total_points
and
entered
getters on your
table1
entity. The corresponding methods would fetch the data for that entity only (via a manual query).
However, in the presence of multiple entities (rows) this results in one of the most infamous issues resulting from ORMs: n+1 queries (or for both getters, 2n+1 queries).
To work around this, a common pattern is to fetch the getter data for all of the entities in one query and use the results to hydrate the getters:
Create setters on your entity:
PHP:
public function setTotalPoints(int $totalPoints): void
{
$this->_getterCache['total_points'] = $totalPoints;
}
// ...
And then where you're fetching the entities:
PHP:
// ...
$entities = $finder->fetch();
$db = \XF::db();
$userIds = $entities->pluckNamed('user_id');
$data = $db->fetchAllKeyed(
'SELECT user_id,
SUM(points) AS total_points,
COUNT(*) AS entered
FROM table2
WHERE user_id IN (' . $db->quote($userIds) . ')
GROUP BY user_id',
'user_id'
);
foreach ($data as $userId => $row) {
$entity = $entities[$userId];
$entity->setTotalPoints($row['total_points']);
// ...
}
This will give you the data you need (
$entity->total_points
etc.) on all of the entities in two total queries. Normally you would abstract this into a repository method so you can call it where ever you may need it. Of course this is all a bit complex, so I'd just focus on getting it working and understanding how the pieces fit together first.