I have two models in cakePHP, one is Parts and the other is Orders.
Parts is a simple model with an id column plusaction multiple fields such as customer, part number, description etc. Each part has its own unique id.
Orders is another simple model with a field called *part_id* which represents an id from the Parts model.
In an action, in the Orders controller I wll need to pull all the orders for that particualr view, find information about that order from the Parts database and put it together in an array ready to be passed to the view.
For example that tables might look like:
ORDERS
Part_id id
2 5
1 7
PARTS
customer partnumber description id
Google XHA-V1 widget_1 1
Yahoo YVS-XN widget_5 2
Since I will have 50-100 orders in any one view, I need an efficient way to reference each order's *part_id* in the Parts database and combine this into one array. Its the searching of the Parts database multiple times that I'm unsure how to achieve effectively.
Note: I guess the solution could be cake or pure PHP.
If you have correctly defined the relation in your Order Model, you should be able to retreive all the corresponding Parts
$this->Orders->find('all');
cakephp will do it in two queries:
- a SELECT for all the Orders
- a SELECT for all the Products where product.id in ( list_of_Orders_ids )
and then it will contruct an array in a proper format
If you need an special query, you could also "manually" set the joins for the query, but you'll need to set the recursive to -1:
$this->Orders->find('all',array('recursive'=>-1,
'fields'=>array('Order.*','Part.*'),
'joins'=>array(array('table' => 'parts',
'alias' => 'Part',
'type' => 'INNER', //or left
'conditions' => array('Part.id = Order.part_id')))))
Good Luck!
You can get an array of (all) Orders using the $this->Order->find('all', array('recursive' => 2)); in your controller. Each order also contains the related Parts information.
Imo customer information should be part of the Order, not of Part. Also you might want Orders to consist of multiple Parts.
Related
Given a string of text as search criteria, in every field in a (very large, 50+ columns) table.
there is one main table and many smaller ones that are connected to it, but have 'ids' in the main table, not actual searchable text values.
such as:
TABLE SALES LOCATIONS
id location_name customer_id ... other fields
2 normalville 4
where customer id is the primary key in another table:
TABLE CUSTOMER
id name industry
4 EXC Selling Things
is there any elegant way to accomplish this with SQL without using a whole slew of joins or subqueries specifically targeted at each of the 50+ fields?
there are probably 11 fields in the main table that are actually just ids pointing to other tables.
also of note, I am using the Yii framework.
Thanks!
i have a model setup in Eloquent of a many to many relationship of Pages and Articles. My users have the ability to order these articles per page.
So my question is, can i order the relationship with sync...
$page->articles()->sync($specificOrderOfArticleIds)
And it sync the relationships in the order of that passed array.
so when i query the page
$page->articles() // in specified order
It's in the specified order.
Maybe i'm missing something obvious here?
In order to do that, you'll need some kind of column in the pivot table that defines an order. For example, an "order" column on the pivot table could be given a value using:
$page->articles()->sync([1 => ["order" => 1], 42 => ["order" => 6]]);
Then on the read, you can pass a closure into your query to specify the order be by this column.
$page = Page::whereId($id)->with(['articles' => function($query) {
$query->orderBy('pivot.order', 'asc');
}])->get();
The "pivot" keyword is used in relationship queries to point to the middle table.
However, you can use the closure ability shown above to order by anything you like, not just the pivot table. Depending on how you define the order, you may not even need the new pivot table column.
This is a "meta" question that I am asking in a effort to better understand some tough nuts I've had to crack lately. Even if you don't get precisely what I'm reaching for here or there is too much text to read through, any practical input is appreciated and probably useful.
Assume you have a website that needs to use data that is stored in multiple tables of a database. That data will need to be iterated through in a multitude of ways, used for calculations in various places, etc.
So on a page that needs to display a collection of projects (from one db table) that each contain a collection of categories (from another db table) that each contain 1 or more items (from another db table) what is the best way to gather the data, organize it and iterate through it for display?
Since each project can have 1 or more categories and each category can have one or more items (but the items are unique to a specific category) what's the best way to organize the resulting pile?
My goal in the below example is to generate a table of projects where each project has the associated categories listed with it and each category has the associated items listed with it but I also need to aggregate data from the items table to display next to the project name
A Project Name (43 items and 2 of them have errors!)
- category 1
- item 1
- item 2
- category 2
- item 1
Another Project Name (12 items and no errors)
- category 1
- item 1
- category 2
- item 1
What I did was to retrieve the data from each table and stick it in a variable. Giving me something like:
var $projects = array("id" => 1, "proj_id" => 1, "name" => "aname");
var $categories = array("id" => 1, "cat_id" => 1234, "proj_id" => 1, "cat_name" => "acatname");
var $items = array("id" => 1, "item_id" => 1234, "location" => "katmandu");
Then I went through the variables in nested foreach() loops building the rows I needed to display.
I ran into difficulties with this as the foreach() loop would work fine when building something 2 levels deep (associating categories with projects) but it did not work as expected when went three levels deep (I N C E P T I O N .. hah, couldn't resist) and tried adding the items to each category (instead adding all of them to one item... first or last I don't recall which). Also, when something was present in the third level of the array, how would you add up that data and then get it out for use back up in the top level of the array being built?
I suppose I could have constructed a mega SQL query that did it all for me and put everything into a single array, saving me the loop confusion by flattening it out, but... well, that's why I'm here asking you all.
So, I suppose the heart of this question is: How do you handle getting lots of data from different tables and then combining it all for display and use in calculations?
Sounds like you're going to want to use SQL JOINs. Consider looking into them:
http://www.w3schools.com/sql/sql_join_left.asp
They'll pull data from multiple tables and aggregate it. It won't produce quite what you're looking for, but it will produce something that you can use in a different way.
is Hadoop the sort of thing you're looking for?
In an ActiveRecord (CakePHP flavored) setup I have a HasAndBelongsToMany association setup on Videos and Bins: A Bin can store n references to Videos, and Videos can belong to n Bins.
I need to be able to manually set and store the display order of the Videos within a particular Bin (so the client can have his Videos in a particular order within the Bin.) My initial thought is to create a field in Bin that stores an array of Video IDs in the order they need to appear. Is this the most efficient way to handle this?
If so, when I then get the HABTM query result, what is the cleanest/most efficient way to re-order the returned query to match the sorted array of ID's?
The Videos associated with a Bin are returned as an array:
[Video] => Array
(
[0] => Array
(
[id] => 49b2de95-0338-48b9-bc88-01ba35664358
...
)
[1] => Array
(
[id] => 49b1ccad-b770-410e-be46-03a035664358
...
)
Or is there a better way to achieve what I'm trying to do without using HABTM associations?
Thanks in advance -
What to do when HABTM becomes complicated?
By default when saving a HasAndBelongsToMany relationship, Cake will delete all rows on the join table before saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2 children. The Club will only have 2 Children, not 12.
Also note that if you want to add more fields to the join (when it was created or meta information) this is possible with HABTM join tables, but it is important to understand that you have an easy option.
HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo association.
Consider this example:
Child hasAndBelongsToMany Club
Another way to look at this is adding a Membership model
Child hasMany Membership
Membership belongsTo Child,
ClubClub hasMany Membership.
These two examples are almost the exact same. They use the same amount and named fields in the database and the same amount of models. The important differences are that the "join" model is named differently and it's behavior is more predictable.
In your example, you need a way to add and remove without editing other users Video links, therefore standard habtm will not suit you very well. Create a model for this "join" similar to the Membership model described above. Further, if you added a weight field, you could use the ordered behavior (that I wrote) to order each set of videos per bin. Then you would use the following fields
id, bin_id, video_id, weight
And set up bin_id as the 'foreign_key' in the behavior configuartion. Good luck
Well I tried to solve just this problem and think I found the simplest solution possible:
When saving a new order of the related models you delete the existing relations and add the new ones in the desired order. This means the ids created run in your sort order and you can simply sort by id to display the order.
public $hasAndBelongsToMany = array(
'Item' => array(
'order' => 'ItemFoldersItem.id ASC, Item.name DESC',
)
);
Pretty simple isn't it?
Don't know about "most efficient" way to implement what you want, that depends on your whole application and what you want to accomplish. I'd just keep in mind that the most simple solution is often the best.
However, I can give you a tip for sorting your Video array! It is fairly easy:
$yourData = Set::sort($yourData, '/Video/id', 'asc');
Take a look at the Set class for more candy. :)
So I have a User table and a History table with User hasMany Histories, and I'm trying to implement pagination on the user table.
My problem is that I have search, and some of the things one can search by are things in the History table. Is there a way to filter pagination results based on data in a table associated by hasMany? Containable, which initially seemed like a solution, allows such filtering but only in the retrieval of associated data, not the records themselves (unless I'm missing something?)
Has anyone had to solve this before?
Since it's a hasMany relationship, that means Cake will need to make 2 separate queries: 1 on the users table, and one on the histories table to retrieve all the associations. Since the History data isn't being retrieved until the 2nd query, then your 1st query cannot be filtered via WHERE conditions for fields found in the History model.
To resolve this, you can do one of two things:
Perform pagination on History using Containable (since History belongsTo User, meaning only 1 query will be performed).
Perform pagination on User the way you're already doing, except perform an ad-hoc join to History such that it's no longer a hasMany relationship.
e.g.:
$this->User->bindModel(array('hasOne' => array('History')));
$this->paginate['User']['contain'][] = 'History';
$this->paginate('User', array('History.some_field' => 'some_value'));