When not to use Containable Behavior in CakePhp 2.x - cakephp-2.0

After a few times adding Containable Behavior to my various model classes, I have a good mind to simply chuck the line into AppModel instead and thus make every model Containable. Which then makes me wonder, is there any situation where it is not desirable or counterproductive for a particular model to have Containable Behavior?

I would say too few to be worried about. I put containable in App Model:
class AppModel extends Model {
public $recursive = -1;
public $actsAs = array('Containable');
}
Containable overrides recursive anyway, so you don't really need to set recursive to -1, but I do just for clarity. Always using containable forces you into the best practice of always using only what you want/need. For small apps, it's not the end of the world to just use recursive and ignore containable, but it's still not best practice.
So, I guess the only argument for using recursive instead of containable would be that in small apps, you save yourself a tiny amount development time, and it won't really noticeably affect performance. I'd definitely go with using containable by default, and removing it where you deem it overkill, rather than the other way around.

Containable can be dangerous b/c Cake acts in an extremely inefficient manner in order to get the nested results.
This site explains it well...
http://www.endyourif.com/cakephp-containable-statement-pitfalls/
Basically though the nice array you are getting back is the results of many different queries while your best performance may come from using a single query with joins.
The convenience of containable is undeniable though.

Related

Proper way of handling scopes with pivot involved

As a laravel user for a couple of months now, I'm trying to better understand advanced use of Eloquent.
I ran into a case where I can't come up with a solution that feels right.
I've got the following structure (simplified)
Mandate
id
status_id
Mandate_user
mandat_id
user_id
link_status
User
id
I declared belongsToMany in both User and Mandat via the pivot table.
on User:
public function mandates(){
return belongsToMany(..)->withPivot('link_status');
}
I'm able to get accepted mandates for a user by using
public function acceptedMandates(){
return $this->mandates()->wherePivot('link_status', MandateUserStatus::Accepted);
}
This works but I'm wondering if there would be a better way by using scopes or other eloquent methods.
And I'm trying to get accepted mandates that also have a status_id lower than 4 (which comes from an enum as well)
I thought of something like :
public function runningMandates(){
return $this->acceptedMandates()->where('status_id','<', 4);
}
Then gathering mandates like so:
$mandates = User::find(1)->runningMandates();
But would the eloquent way be of doing something like:
$mandates = User::find(1)->mandates()->running()->accepted();
Thanks for your time.
It's a very subjective question and hard to give a straight answer to, but hopefully I can stop you from second-guessing yourself: what you're doing is perfectly fine and no less "eloquent-like" than the other.
Personally I like what you're doing now much more than what you present as the "Eloquent way" and have done so before in professional projects, but ultimately there is a difference in the way you design the code for a framework or package, which must be flexible to account for many different scenarios, most of which you cannot even envision when doing version one, versus how you design the code for an app which will only be consumed by itself. If you know the business logic that drives your app and the views it will present to its users (subsequently, the queries it must perform), why wouldn't you create methods that will easily achieve just that?
Eloquent relies on chaining not just because it's cool, but because it does not know (or care to know) what your business logic is. To me, the "Eloquent way" is more about fluent, readable code, and I find that $user->runningMandates is more readable than $user->mandates()->running()->accepted()->get().
I also find that the first approach is easier to get into. If you're constantly forcing yourself to separate methods so they can be chained, it can be harder to grasp which method is doing what and which method relies on which. Query scopes can modify the query as they like (join, alias, etc) so the danger lies in one method requiring another to come first, because you're conditioning based on a table or column that isn't on the original query, so a method will only work in conjunction with another; or maybe two methods are trying to join the same table, or using the same aliases, so they cannot be used together. It may seem far-fetched, but often the effort to keep things separate and tidy will make your code harder to use. But even if things don't blow up code-wise, the deal-breaker to me would be neglecting business logic: in your case, does it make sense to have two separate scopes for running and accepted? Can a mandate be running if it's not accepted? If not, you should try to prevent a less knowledgeable developer from creating that flawed query (logic-wise). And as you said, the structure is simplified so there may be other gotchas lurking around, and many more will come as the app grows in complexity.
If I'm taking over someone else's code, I'd rather have limited, self-contained methods that don't break over simple, decentralized methods that require you to read (often non-existent) documentation in order to prevent the many ways in which to mis-use them.
Basically, scopes add constraints to the query so you may use the scope approach to modify the query by calling scope methods that will eventually give you a chance to make a query dynamically. So, you may declare (as you did) one relationship method where you may do all the query and call that specific method or use scope methods two build the query using method calls where each method adds a constraint. In this case, it'll be more dynamic but still it's a preference and it gives you more flexibility (IMO). So yes, you can use query scopes for that, for example:
// Declare the main relationship
public function mandates()
{
return $this->belongsToMany(..)->withPivot('link_status');
}
Now, declare query scope for accepted (add a constraint on the query returned by mandates method call)
public function scopeAccepted($query)
{
return $query->wherePivot('link_status', MandateUserStatus::Accepted);
}
Now, add another query scope for running, for example:
public function scopeRunning($query)
{
return $query->where('status_id','<', 4);
}
Now, if you call something likethe following:
$user = User::find(1);
Now, call the relationship method (not as property)
$mendates = $user
->mandates() // The method is called and a query object is constructed
->running() // Add another constraint into the query: ->where('status_id','<', 4)
->accepted() // Add another constraint into the query: ...
->get(); // Finally, execute the query to get the result
Probably, it's clear to you now. Notice the method call mandates(), it's a method call on the relation defined which returns the Query Builder and by chaining additional scope method calls, you are just modifying the query by adding some more constraints but you can do all the query in one method without dynamic scopes, so it's up to you. While, scopes gives you more flexibility but it doesn't mean you've to follow this approach always, it depends.

Cakephp 3: Calling Table functions from Entity is a bad or good idea?

When I have some entity and I wanna save, validade or delete. Why do I have to call the Table method? For example:
$articlesTable = TableRegistry::get('Articles');
$article = $articlesTable->get(12);
$article->title = 'CakePHP is THE best PHP framework!';
$articlesTable->save($article);
Why isn't like this:
$article->save();
or $article->delete();
It's very simple to implement:
On my Article Entity I can do it like:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
public function save()
{
$table = TableRegistry::get($this->source());
$table->save($this);
}
}
It's working, but I would like to know if its a bad practice or a good idea.
Thanks in advance :)
TL;DR: Technically you can do it for the high price of tight coupling (which is considered bad practice).
Explanation: I wouldn't consider this best practice because the entity is supposed to be a dumb data object. It should not contain any business logic. Also usually it's not just a simple save call but there is some follow up logic to implement: Handle the success and failure of the save and act accordingly by updating the UI or sending a response. Also you effectively couple the entity with a specific table. You turn a dumb data object into an object that implements business logic.
Technically you can do it this way and I think there are frameworks or ORMs that do it this way but I'm not a fan of coupling things. I prefer to try to write code as lose coupled as possible. See also SoC.
Also I don't think you'll save any lines of code with your approach, you just move it to a different place. I don't see any benefit that would justify the introduction of coupling the entity to the business logic.
If you go your path I would implement that method as a trait or use a base entity class to inherit from to avoid repeating the code.

CakePHP find with "contain"

My model has many relationships to other tables/models, if i use contain parameter, for example to order by field in results of one model, all other tables in query results are missing, and must manually written in contain parameter, how can i get all related models data without manually writing it?
$data = $this->Cv->find('first',
array(
'contain' => array('Cv_experience' => array('order' => 'Cv_experience.start_year desc'))));
It sounds like you've been relying on the Model's recursive property. Once you use 'contain', those models that used to come in automatically no longer do, right?
The answer is, you should be using recursive OR contain, not both. If you're using contain, you should set recursive to OFF ($recursive = -1;).
As far as which to use, it's HIGHLY recommended to not use recursve at all. Set it to -1 in your AppModel, and use Containable Behavior any time you want additional data. Recursive is bad practice for anything but a simple blog app, and I believe is even being completely removed in Cake 3.
Note on ordering: You cannot order by a contained model. 'Contain' creates multiple queries, so - if you want to do what you're trying, you'll need to use JOINs.

CakePHP 2.3: Filter find results based on current user

I have models with the the following relations, defining a situation where users can belong to many groups, and multiple groups can be granted access to a project.
User HABTM Group HABTM Project
I would like to set things up so that any find() done on the Project model will only return results to which the current user has access, based on her group membership.
My first thought is to use the beforeFind() callback to modify the query. However, the two-level association has me stumped. I solved a similar problem (see this question) by rebinding models. However, that was for a custom find method—I don't think that approach will work in a general situation like this where I need to modify arbitrary queries.
Using afterFind() to filter results isn't a good idea because it will confuse pagination (for example) when it doesn't return the right number of records.
Finally, I have a nagging suspicion that I'm trying to re-invent the wheel. The access control I've seen in CakePHP (e.g. Cake ACLs) has been at the controller/action level rather than at the model/record level, but I feel like this should be a solved problem.
Edit: I eventually decided that this was over-complicated and just added a getAccessibleByUser($id) method to my Project model. However, I'm still curious whether it's possible to globally add this kind of restriction to all find() operations. It seems like exactly the sort of thing you'd want to do in beforeFind(), and I suspect (as DavidYell suggests below) that the answer may lie with the Containable behavior.
You should look at the Containable behaviour. If you are using CakePHP 2.x then it comes in the box.
This behaviour allows you to manage the model relations and the data which is returned by them, along with allowing you to pass conditions, such as a group_id into your contain.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html

CakePHP - deep model binding vs uses

I recently read an old article it which this was suggested
Avoid using the $uses array You’ve got two completely unrelated models, but you need info from one in the controller of another. The
first idea is to add them to the $uses array. Hey, it’s easy and gets
the job done. Well, to make the long story short, it’s bad practice.
Think about your model bindings and ensure that models are really not
related to one another. Sometimes your User model is definitely not
related to CommentRating, but you absolutely need it in your users
controller. Well, just by chance it appears that
User->Post->Comment->CommentRating. It’s a deep binding that may not
be obvious at first, but by employing such a chain of models you can
easily avoid using the $uses array, when it’s really not necessary.
For the sake of readability/maintainability, I tend to prefer putting CommentRating in the uses array and then calling $this->CommentRating directly.
Is using the $uses array really bad practice and if so why?
That quote cuts a long story a bit too short. Why is it a bad practice? He doesn't even give one reason. I wouldn't pay too much attention to it.
I'm no expert, but there are definitely cases when readability and common sense in your code is more important than a minute performance increase or whatever else avoiding $uses may provide. I'll be continuing to use $uses instead of obscure model chains like User->Post->Comment->Rating
EDIT: And as #tigrang added, with lazy loading of Models in Cake 2, there's not really a performance benefit anyway!

Resources