i need to add to an "Event" model some information about "sub-events". For example, the Event might be a Match and a sub-event can be a goal (I need to track the striker too, and the minute), substitution or other. How is the best method to implement this? I think that is not a good solution to create a new Model for a Goal, I want that all the informations stay inside the Match model.
Maybe it can be done via "belongs to itself" association.
Look at this: Rails 3 - A model with a one to one relationship to itself - do I need belongs_to
The answer suggest using ancestry gem, or simple belongs_to association:
belongs_to :event, :foreign_key => "parent_event_id"
Also, in your model, you should add "event_type" column and put 'match', 'goal', or whatever you want here.
In my opinion it IS good solution to create Goal model, that would belongs_to a Match and a Striker and would store the information when it has happened (the minute).
Related
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.
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
I'm using activeadmin 0.6.1, mongoid 3.1.5, and activeadmin-mongoid 0.3.0. embeds_many relations are arguably the best thing about mongoid, reducing the need for additional queries to fetch related data.
But I can't find a good way to make them work with activeadmin's f.has_many form helper (and activeadmin-mongoid doesn't seem to provide a f.embeds_many version).
The best solution I've come up with so far is to use a has_many relation, and use mongoid-alize to denormalize the has_many fields into the parent object. But this is somewhat unwieldy, as it requires me to access them as parent.children_fields instead of parent.children, which interferes with any code that expects an array of child objects instead of an array of attribute hashes.
I am relatively new to Rails.
I have a User model through Devise. I am wondering if it is more efficient to have all the additional fields i need for the user, in a separate Profile model.
I am coming across similar situations where I am considering creating a new model and using a has_one association to that model however it seems like maybe it will be cleaner if I had all the attributes belonging to a user within the User model. How do you deal with such situations? What effect will it have on application performance?
Can someone elaborate on the advantages and disadvantages of creating has_one relationship, especially in terms of performance.
I am also relatively new to Rails as well, but this is my take...
The benefits of associations in Rails are pretty obvious I think. In this specific case I think you are fine to go either way. Here are some things to consider...
If you use a has_one relationship, you must remember that when you are referencing the profile you will end up with something similar to this:
user = User.first
puts user.profile.first_name
puts user.profile.age
Simple enough, but if you wanted something like user.first_name you would need to delegate that method to the Profile model. This is all a matter of preference.
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).