Philosophy of Table vs Entity

I’m a bit confused about the intent, purpose, and execution behind separating model code into Table and Entity.

In my view, if Table code is supposed to be about talking to the database, then most of the business logic would live in Entity code.

But, CakePHP 4 has all the relationships defined under Table code.

It doesn’t make sense to me to have the relationships defined there, and then use and refine those relationships in a totally different place. Especially considering the database itself has no care about the relationships. CakePHP defines and enforces relationships and the database just stores raw data. Relationships have nothing to do with transferring data to and from the database (aside from preparing the right query to transfer associated data)… nor do relationships have anything to do with formatting on write or translating on read, nor with validating (all of these being examples of functionality that, to me at least, seem right for Table code).

As an example, imagine coding an application that’s designed to manage menus and recipes, taking into account recipe type, ingredients needed, ingredients on hand, chefs available, and seasons/occasions. Now, when the head chef wants to develop a menu, she will load the Menu model from Cake and it will need to pull in data from all those other tables, and have the ability to constrain the list of possible recipes based on those other factors, as well as factors unique to the Menu model (say, a limit of 10 entrees and 5 desserts, minimum profit margins and gross receipts based on price per recipe, and actual cost per recipe, and expected service).

It seems to me that all that logic would be housed within the Entity code for the Menu model. Whereas, data integrity would be managed in the Table code (and be fairly thin, relative to the Entity code).

But… as I said above: CakePHP has all those relationships defined in the Table code. It’s extremely awkward to be separating the relationships from the relationship utilization and management. The fine details and conditions of those relationships are all going to be living in Entity code.

Or am I wrong? Should I be putting all that logic into Table code? If so, what exactly is Entity code for then?

Apologies if this is actually discussed in the docs. I was unable to find it.

Reading your hypothetical menu entity is done by the table class. If relationships were handled by the entity, then it couldn’t do things like joins and grouping and matching until after the entity had been read, but those things are all needed in order to generate the correct SQL queries to fetch the records that you need.

Also, validation quite often needs to know about relationships, as it may need to do things like check that an ID that a record references actually exists in another table.

I think you’re putting too much expectation on what entities are supposed to do. They are for dealing with a single record only, and should have very little in the way of business logic in them.

1 Like

This seems an interesting and (to me) unexpected reply.

I think I have my understanding of Entity vs Table wrong in that case.

But then, I am having trouble understanding what Entity would be in charge of in the Menu example. After all, if the head chef is creating a new menu, instantiating a new copy of (say) a “template” menu… then all of that seems (again, this is my perception) to be related to a single menu entity at the time.

(I would suggest that, in a universe where my initial understand was correct, that it would still be proper to use relationship logic in Entity as opposed to Table… but, where Table would have to get the relationship information from Entity.)

The point is that the entity class might exist, but the entity object could not exist until after the query happens, at which point it’s too late for the relationships that it defines to be used to fine-tune or optimize the query.

Many people, I think, don’t use entity objects for anything at all, apart from being a dumb container for data. Business logic placed in entities is generally restricted to accessor functions for manipulating that data, like concatenating first_name and last_name fields together so you can use $person->full_name, or hashing the password when you assign it into the entity so that it’s the hash that gets saved to the database.