Search record in HABTM relationship - CakePHP - cakephp

I have a stores and store_details table and now a store_details_stores table.
The Store model has the following:
public $hasAndBelongsToMany = array('StoreDetail');
The StoreDetail model has the following:
public $hasAndBelongsToMany = array('Store');
When I attempt the below query in the stores_controller, I receive an sql error. For some reason the store_details table is not being joined naturally. Is this expected behaviour? Do I need to join this table manually?
$this->Store->find('all', array('conditions' => array('StoreDetail.name' => 'Parking')));

This is expected behavior. You need to set up a bindModel call with your conditions, or you can create a model for your join table and query that directly.
See:
CakePHP Book - HABTM
Modelizing HABTM Join Tables
$this->Store->bindModel(array(
'hasAndBelongsToMany' => array(
'StoreDetail' => array('conditions'=>array('StoreDetail.name' => 'Parking')
))));
$this->Store->find('all');

I would recommend applying the condition to the association before you do your find, using the bindModel method.
/**
* Apply a condition to the association
*/
$this->Store->bindModel(array(
'hasAndBelongsToMany' => array(
'StoreDetail' => array('conditions' => array('StoreDetail.name'=>'Parking'))
)));
/**
* Find all Stores that have an associated StoreDetail.name of 'Parking'
*/
$this->Store->find('all');

Related

Error: Table tablename for model modelname was not found in datasource default with non-default database

This is with CakePHP 2.4. I have a table (table1) which is connected to three others (tables 2 to 4) through a hasMany through connection. All four tables are in a non-default database. Although I'm using hasMany from tables 2-4 to table 1 and in the class for table1
public $belongsTo = array('table2', 'table3', 'table4');
When I try and display the view for table1 I get the error
Table table1 for model model1 was not found in datasource default
So it's looking in datasource default even though all four tables have are in the non-default database (specified using $useDbConfig).
Although the documentation doesn't say why you would want to specify the classname, even if it follows the naming convention, this appears to be the solution:
public $belongsTo = array('table2', => array(
'className' => 'table2'),
'table3'=> array(
'className' => 'table3'),
'table4'=> array(
'className' => 'table4'));

CakePHP find on a join table

Sorry if this is obvious - I'm trying to do a find() on a join table and failing, whats the correct syntax to do this?
Basically I have expenseCode table with a HABTM releationship with the expense table.
In my expenseCode model I have:
public function beforeDelete($cascade = false) {
$count = $this->Expense->find("count", array(
'conditions' => array('expense_code_id' => $this->id)
));
if ($count == 0) {
return true;
} else {
//$this->Session->setFlash('Record cannot be deleted as it has ' . $count . 'number of expenses attached to it');
return false;
}
}
If I uncomment the setFlash() I get the error.
Where am I going wrong? Thanks in advance.
Unfortunately it's not possible to perform such a query on the HABTM join table from one of the parent models without some extra work. I'm assuming that Expense is the parent model and ExpensesCode the join model?
A common way is to modelise the HABTM join table. Say you have an expenses and codes table which are joined by expenses_codes:
$this->Expense->bindModel(array('hasOne' => array('ExpensesCode')));
$this->Expense->find('all', array(
'fields' => array('Expense.*'),
'conditions' => array('ExpensesCode.expense_code_id' => $this->id)
));
However, Cake also auto-iniatlises a model for the join table when a HABTM association is defined (see the manual, and the "with" key in the list of possible keys).
So this syntax would let you directly query the join table:
$this->Expense->ExpensesCode->find('all', array(
'conditions' => array('ExpensesCode.expense_code_id' => $this->id)
));
The query above will net you with an array containing only the results from the join table, as it doesn't perform a join like the first procedure. So you would have to perform a second find() on the Expense model to find the expenses related to the expense_code_id from ExpensesCode.
EDIT:
It's a framework convention that HABTM join tables should be underscored and alphabetically ordered. So if the HABTM join table is called codes_expenses, it's auto-modelised as CodesExpense.
You can also work in a simpler way if you create another model with two hasMany relations instead of one with HABTM.
HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo association.
I found it is one of the simplest ways to save and retrieve data.
For more information:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#what-to-do-when-habtm-becomes-complicated

Retrieving values from multiple tables using CakePHP model associations

I have a ProductsController in which I am retrieving Products data and need to also retrieve the Category Name. (Note: My Products table has only Category_ID in it), how can I do that using CakePHP model associations?
I have seen examples in which the ID of the main data table (in my case, Products table) is a Foreign Key in the Associated Table. However, my case slightly different in that the Category_ID (from the secondary table) is part of the Main table (Products table).
I am not able to retrieve the Category Name using CakePHP model config. Can you help?
My ProductsController is on Products table which has
ID
Prod_Name
Category_ID
....
My Categories table is like
ID
Cat_Name
In my ProductsController I want to retrieve Cat_Name for Products being retrieved.
In your Product Model, use the association:
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
When retrieving your Products data use find method:
$this->set('variable', $this->Product->find('all'));
Once its in your View, it is an array containing all the products and its category.
Like this:
<?php
foreach($variable as $itemInTable):
echo 'Product:' . $itemInTable['Product']['Prod_Name'];
echo 'Its Category:' . $itemInTable['Category']['Cat_Name'];
endforeach;
?>
fzmaster's answer is correct. When you have a foreign key in Table A that corresponds to an id in Table B, it is said that the Model A "belongs to" Model B. At the same time, there could be an inverse relationship where Model B "has many" Model As.
The associations are fairly straightforward within that context and if you use the Cake naming conventions, you can associate the models with minimal additional code:
class Product extends AppModel{
var $belongsTo = array( 'Category' );
}
class Category extends AppModel{
var $hasMany = array( 'Product' );
}
At that point, CakePHP's Model::Find() method will automatically retrieve associated models unless you limit it with $recursive or by using the Containable behavior.

CakePHP update extra field on HABTM join table

I have problem with updating (better updating not recreating) extra field in HABTM join table. I searched google and other sources, but struggled for 4 days now.
I have models:
class Tutorial extends AppModel {
var $hasAndBelongsToMany = array(
'TutorialCategory' => array(
'with' => 'TutorialCategoriesTutorial',
'order' => 'TutorialCategoriesTutorial.order_by ASC',
'unique' => true,
);
}
class TutorialCategory extends AppModel {
var $hasAndBelongsToMany = array(
'Tutorial' => array(
'with' => 'TutorialCategoriesTutorial',
'unique' => true,
);
}
join table tutorial_categories_tutorial have id, tutorial_id, tutorial_category_id, order_by fields.
I am trying to update order_by field like:
$order = 1;
foreach($tutorials as $i => $tutorial) {
$this->data[$i]['Tutorial']['id'] = $tutorial['Tutorial']['id];
$this->data[$i]['TutorialCategory']['id'] = $tutorial['TutorialCategory']['id];
$this->data[$i]['TutorialCategoriesTutorial']['order_by'] = $order;
++$order;
}
$this->Tutorial->bindModel(array('hasMany' => array('TutorialCategoriesTutorial')));
$saved = $this->Tutorial->saveAll($this->data);
This is deleting and crating new records in join table, but not setting order_by at all. I want to update record and set now order_by value. I tried hasMany through but no luck.
Please help and/or give advice and explanation.
Thank you!
As you have added extra data (order field) to the HABTM join model, you have actually exceeded the capabilities of a simple HABTM relationship with CakePHP. What you actually need to setup is a hasMany Through relationship.
In your case you'll basically make a "membership" model with Tutorial ID, catergory id and as much data as you want to assign to it. You will then define the relatioships as Membership belongsTo Tutorial & Category. The book probably has a better example than what I've just explained!
The main reason for this is that each "membership" record is treated as a normal record with no HABTM behaviour attached to it, so you can edit, delete and add records individually and easily.

CakePHP multi-HABTM associations with pagination

For an e-commerce app that I'm building I am using CakePHP.
I have created the db and afterwards baked my app with cake bake.
My models are all linked up properly as follows:
Product hasMany CategoryProduct,ImagesProduct
Category hasMany CategoryProduct
CategoryProduct belongsTo Product,Category
Image hasMany ImagesProduct
ImagesProduct belongsTo Product,Image
I have tried in various ways to obtain a paginated view of products dependent of the category_id with no succes.
I have managed to obtain the array I wanted with
$this->CategoryProducts->findByCategoryId($id), but I can't use the built-in paginator from cakePHP with the resultant array.
Could somebody tell me how to properly formulate the $options['joins'] array to pass on to the paginate function in order to get the same result, but in a paginated way?
The SQL for what I want to do would be something like this:
SELECT p . * , i.filename
FROM products p
LEFT JOIN (
category_products cp, categories c, images_products ip, images i
) ON ( cp.product_id = p.id
AND c.id = cp.category_id
AND c.id =2
AND ip.product_id = p.id
AND ip.image_id = i.id )
This is a question that perplexed me for quite sometime. You shouldn't have to associate either of your join models (CategoryProduct, ImagesProduct) directly to your models if you're using a HABTM association with CakePHP. cake bake may not have picked it up the HABTM association correctly if you didn't have the table names quite right. The join tables should be categories_products and images_products, which would make th join models CategoriesProduct and ImagesProduct.
Either way though, the following should get you going on filtering by categories:
//in products_controller.php:
//Fake a HasOne association, setting the reset parameter to false since pagination
//requires two queries to complete (one for the count, one for the data)
$this->Product->bindModel(array(
'hasOne' => array(
'CategoriesProduct
)
), false);
//Group by products since the HasOne will return multiple rows for each product
$options = array(
'group' => 'Product.id',
'conditions' => array(
'CategoriesProduct.category_id' => $categories
)
);
//To paginate the result:
$this->paginate = $options;
$products = $this->paginate();
In this example $categories can be either a single category_id or an array containing category_ids (i.e. array('1', '2', '3')). The result will be an 'OR' of categories. If you're looking to filter by an 'AND' type condition, check http://edblogs.cal.msu.edu/devteam/2011/02/08/cakephp-habtm-searches/.
Hope this helps.

Resources