HABTM and belongsTo at the same join, cakePhp - cakephp

I have a model Fix with a relationship HABTM Device model.
Device model has a belongsTo to Device_type model, like this, for only getting the device type name:
var $belongsTo = array('Device_type'=>array('fields'=>'name'));
So, I need every Fix, its devices and its Device_types. When I make a Fix->find('all', array('recursive' => 2))
I expect to get every Device related to Fix (this works ok) and ALSO for every device, its Device_type.name (which is not working).
This is what I get instead for every Device in the result (an empty array):
["Device_type"]=>
array(0) {
}
Besides this, when I make this query for testing: Fix->Device->find('all'), it returns the current Device_type.names for every device related to fixes, which means models are related propertly.
Any help? Thanks.

First thing I notice, is your naming conventions should be lower case under_score for your multi-word table names.
And its also apparent your relationships most likely are not set up correctly if you are not getting the data on a recursive 2.
It's kind of hard to make more judgement with your limited code.

If you are new to CakePHP and MVC, it would be really best to follow the blog tutorial on the CakePHP web site. From that, you will learn the basics of building a CakePHP app and in the end have working application which you can "play" with and modify to learn how MVC ticks. You can experiment and learn a lot from this : )

Related

What does $this->Model1->Model2 accomplish in CakePHP HABTM?

Basically, I've implemented the HABTM successfully in CakePHP, but the trouble is, I don't understand why it works.
The thing I hate about the CakePHP cookbook is that is tells you what to do but make very little effort to explain the underlying segments of their code.
Essentially, my data model is like this.
Task HABTM Question
I don't understand this code fragment.
$this->set('questions', $this->Task->Question->find('list'))
In particular, what is $this->Task->Question supposed to accomplish?
Also how is the above code link to this code fragment in the view?
echo $this->Form->input('Question');
One thing that is very peculiar is that with the above code fragment, I get a multiple select option.
However, if I change the code to this,
echo $this->Form->input('question');
I get a single select drop down list.
I scoured the entire documentation and still cannot find a satisfactory explanation to my doubts.
Would really appreciate if anyone can clarify this issue for me.
1. Model chaining
When a model has an association to another model (like in your example an HABTM one) then you can call methods of the associated model by chaining it to the current model. This is explained early in Associations and an example of exactly how it works is given at the end of the first section.
When you are someplace in your TasksController normally you would expect that only your Task model would be available. Instead any association described in the Task model is chained to that model in the form of $this->Model1->Model2.
So $this->set('questions', $this->Task->Question->find('list')) means:
From current model Task that you know about, access the associated model Question and then call its find('list') method. Then $this->set the results to the view as variable questions.
2. FormHelper Conventions
When you use a CamelCased single name for field input, like in $this->Form->input('Question'); you are saying to FormHelper that the data contained in the questions variable come from a model named Question with a HABTM association, therefore they should be handled as a multiple select (as HABTM points to such an association).
With a field name of model_id, like in this example question_id, you're asking for a single select (select a single id of the connected model).
With anything else, FormHelper looks at the field definition and takes the decision itself, but of course your can override any default behavior you want using options.
This is explained in detail and I'm surprised you missed both. CakePHP has one of the best documentations available, almost everything you need is there.

MVC: Correct pattern to reference objects from a different model

I'm using CakePHP2.3 and my app has many associations between models. It's very common that a controller action will involve manipulating data from another model. So I start to write a method in the model class to keep the controllers skinny... But in these situations, I'm never sure which model the method should go in?
Here's an example. Say I have two models: Book and Author. Author hasMany Book. In the /books/add view I might want to show a drop-down list of popular authors for the user to select as associated with that book. So I need to write a method in one of the two models. Should I...
A. Write a method in the Author model class and call that method from inside the BooksController::add() action...
$this->Author->get_popular_authors()
B. Write a method in the Book model class that instantiates the other model and uses it's find functions... Ex:
//Inside Book::get_popular_authors()
$Author = new Author();
$populars = $Author->find('all', $options);
return $populars;
I think my question is the same as asking "what is the best practice for writing model methods that primarily deal with associations between another model?" How best to decide which model that method should belong to? Thanks in advance.
PS: I'm not interested in hearing whether you thinking CakePHP sucks or isn't "true" MVC. This question is about MVC design pattern, not framework(s).
IMHO the function should be in the model that most closely matches the data you're trying to retrieve. Models are the "data layer".
So if you're fetching "popular authors", the function should be in the Author model, and so on.
Sometimes a function won't fit any model "cleanly", so you just pick one and continue. There are much more productive design decisions to concern yourself with. :)
BTW, in Cake, related models can be accessed without fetching "other" the model object. So if Book is related to Author:
//BooksController
$this->Book->Author->get_popular_authors();
//Book Model
$this->Author->get_popular_authors();
ref: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#relationship-types
Follow the coding standards: get_popular_authors() this should be camel cased getPopularAuthors().
My guess is further that you want to display a list of popular authors. I would implement this using an element and cache that element and fetching the data in that element using requestAction() to fetch the data from the Authors controller (the action calls the model method).
This way the code is in the "right" place, your element is cached (performance bonus) and reuseable within any place.
That brings me back to
"what is the best practice for writing model methods that primarily
deal with associations between another model?"
In theory you can stuff your code into any model and call it through the assocs. I would say common sense applies here: Your method should be implement in the model/controller it matches the most. Is it user related? User model/controller. Is it a book that belongs to an user? Book model/controller.
I would always try to keep the coupling low and put the code into a specific domain. See also separation of concerns.
I think the key point to answer your question is defined by your specifications: "... popular authors for the user to select as associated with that book.".
That, in addition to the fact that you fetch all the authors, makes me ask:
What is the criteria that you will use to determine which authors are popular?
I doubt it, but if that depends on the current book being added, or some previous fields the user entered, there's some sense in adopting solution B and write the logic inside the Book model.
More likely solution A is the correct one because your case needs the code to find popular authors only in the add action of the Book controller. It is a "feature" of the add action only and so it should be coded inside the Author model to retrieve the list and called by the add action when preparing the "empty" form to pass the list to the view.
Furthermore, it would make sense to write some similar code inside the Book model if you wanted, e.g., to display all the other books from the same author.
In this case you seem to want popular authors (those with more books ?), so this clearly is an "extra feature" of the Author model (That you could even code as a custom find method).
In any case, as stated by others as well, there's no need to re-load the Author model as it is automatically loaded via its association with Books.
Look out for Premature Optimization. Just build your project till it works. You can always optimize your code or mvc patterns after you do a review of your code. And most important after your project is done most of the time you will see a more clear or better way to do it faster/smarter and better than you did before.
You can't and never will build a perfect mvc or project in one time. You need to find yourself a way of working you like or prefer and in time you'll learn how to improve your coding.
See for more information about Premature Optimization

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

Backbone Relational and subviews, best "save" strategy

I'm using Backbone-relational like this:
class window.Car extends Backbone.RelationalModel
class window.Person extends Backbone.RelationalModel
relations: [{
type: Backbone.HasOne
key: 'car'
relatedModel: Car
}]
There is also a PersonView, which embeds a subview CarView.
Now my question is, what is the best strategy when the user clicks "Save" in the PersonView? The problem is that the save will happen in two steps, first the car then the person. But what if validation fails with the person? It will cancel the save, but the car will be already saved!
Maybe Backbone-relational is not the best option here? Any alternative?
More generally, I'm more and more frustrated with Backbone playing not very nice with deeply embedded documents (I'm using MongoDB). Yes, the Todo app is nice, but the real world is more complex! Any guidance or tutorial would be very much appreciated.
It’s difficult to answer without to know the details, but, are you sure that you need relational models in the browser side?
Backbone is designed for restful applications. Is your API in the server side restful?
In your case (and without really understanding the constraints you have) I can think of the following implementation.
In the server the following URIs API:
[…]/carType/{carType}
[…]/persons/{person}
[…]/cars/{car}
In this implementation, “car” represents an actual physical object where “carType” represents a class of car. The backbone model for “car” contains the ID for the “carType” and the ID for the “person”. There are also backbone models for “carType” and “person”.
In this way, when you want to associate a “person” and a “carType” you create a new “car” and make a POST to the server. As “car” is its own independent object (and has its own URL), you can operate in a transactional way with it (that is what, I think, you are asking).
I hope it helps and the answer its not very far of what you are actually trying to do.
The best save strategy would be to save the whole thing atomically (in one step). Otherwise, you're always going to have these type of problems where failing to save one object on the server means you're going to have to destroy other objects on both the server and the client.
To support that, Backbone-relational has excellent support for serializing and deserializing nested objects.

Find() one of each matching field CakePHP

Trips hasMany Legs
Airports has no associations
How can I find the cheapest trip for each destination airport using CakePHP?
Right now, the only thing I can think of to do is to foreach through an array of airports. This would require hundreds of queries to the database (which I think is not the fastest way of doing it).
function getCheapestTrip($origin){
$airports=$this->Airport->getAirports();
foreach($airports as $airport):
$cheapest_flights=$this->Trip->find('first',
array(
'conditions'=>array('Leg.origin'=>$origin, 'MIN(Trip.price) as price'),
'fields'=>array('Trip.origin','price','Leg.destination','Leg.depart','Leg.arrive'),
'recursive'=>2,
));
endforeach;
}
}
Also, I think that this data type stuff should be in the model per CakePHP conventions (Fat models, skinny controllers). I read that to call a different model's function such as getAirports I can use loadModel but I found that in CakePHP's controller method section. How should one get another model's data/model function into anothers?
Thanks!
The answer to your second question, "How to load a model within another model?" can be found here.
If you're looking for a better algorithm rigth now I do not have the solution.
Mine is a design solution: basically you should add a field to your destination airport which will be updated every time you add a new flight so you have your information directly in your destination record.
This stands if I have understood your problem. I'm not english so I'm not familiar with the semantic of "leg" associated to a trip (to me it's a body part)
The problem you're solving is the Traveling Salesman Problem: http://en.wikipedia.org/wiki/Travelling_salesman_problem
From what I've read on how google maps does it, you'll want to precompute your most common routes and connections. Keep that precomputed info in a cheap cache (memcache prolly). Basically, you won't be able to recalculate each time, so calc a few common ones and build a precomputed cache.
WRT the algorithm, some google searching will be your friend for tips and tricks. This problem has been solved many times (none are exactly computationally efficient, which is why you should precompute and cache).

Resources