Overriding CakePHP conventions. One table with 2 columns of the same model - cakephp

Suppose I have a table/model Products.
Suppose I have a table/model Equivalencies which has among its columns this 2:
original_id
equivalent_id
Both original_id and equivalent_id have as foreign keys the primary key of Products. So, I can't follow the convention of naming original_id as product_id, because I also have to deal with equivalent_id (which would also be product_id).
What should I do?
Currently, I have the Product model configured as this:
public $hasMany = array(
'Original' => array(
'className' => 'Equivalency',
'foreignKey' => 'original_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => '',
'table' => 'products',
),
'Equivalent' => array(
'className' => 'Equivalency',
'foreignKey' => 'equivalent_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
Could anyone tell me if this is how it's supposed to be done?
What I Want
I want to be able to find all the equivalencies of a given product, but with conditions over the relation (the one with original_id, equivalent_id).
This doesn't work:
$original = $this->Product->find('all',
array(
'contain' => 'Product.Original.deleted_equivalent = false',
'conditions' => array('Product.id' => $id)
));

Your model is fine, that's the way you should do it.
Your call to find is not.
Try this
$original = $this->Product->find('all',
array(
'contain' => array('Equivalent'),
'conditions' => array('Product.id' => $id)
));

Related

CakePHP adding to multiple tables from one view

can someone provide an explanation for me?
I've got a database where I'm trying to update 4 tables from one page, Organizations, OrganizationsDetails, Account, AccountRoles. The add view itself would contain fields for the Account and the Organization. When I call the OrganizationsController add function, I want to then call add on AccountController, AccountRolesController (set Account as an administrator role), and OrganizationDetailsController (set that Account as the administrator for that specific Organization).
Should I be using components for this? When I call add() on the Organizations, I always intend to perform these steps - never solely create the Organizations entry below. Should I be doing this directly in that function or some other method?
Let me know if I need to explain more, thanks!
EDIT: Added the four models below:
Organization
class Organization extends AppModel {
public $hasMany = array(
'Account' => array(
'className' => 'Account',
'foreignKey' => 'organization_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $hasOne = array(
'OrganizationDetail' => array(
'className' => 'OrganizationDetail',
'foreignKey' => 'organization_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
OrganizationDetail
class OrganizationDetail extends AppModel {
public $belongsTo = array(
'Organization' => array(
'className' => 'Organization',
'foreignKey' => 'organization_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Account' => array(
'className' => 'Account',
'foreignKey' => 'account_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
AccountRole
class AccountRole extends AppModel {
public $belongsTo = array(
'Account' => array(
'className' => 'Account',
'foreignKey' => 'account_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Role' => array(
'className' => 'Role',
'foreignKey' => 'role_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Account
class Account extends AppModel {
public $belongsTo = array(
'Organization' => array(
'className' => 'Organization',
'foreignKey' => 'organization_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $hasOne = array(
'OrganizationDetail' => array(
'className' => 'OrganizationDetail',
'foreignKey' => 'account_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $hasMany = array(
'AccountRole' => array(
'className' => 'AccountRole',
'foreignKey' => 'account_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
You've got wrong the whole MVC idea. Controllers should not call methods in other controllers (or at least not in this scenario).
The right approach would be to set up OrganizationsController->add() so that it saves all the data by independent calls to each Model->save($data), or by using
Organization->saveAssociated($data,array('deep' => true))
Your form fields need to be named properly for this to work. For example:
echo $this->Form->create('Organization', array('action' => 'add'));
echo $this->Form->input('Organization.name'));
echo $this->Form->input('Account.0.name', array('label' => 'Account name'));
echo $this->Form->input('OrganizationDetail.details', array('label' => 'Org. Details'));
echo $this->Form->input('Account.0.AccountRole.id', array('label' => 'Account Role'));
echo $this->Form->end('Add');
However, you may get into trouble if you try to go too many levels deep. If you find yourself in this situation, it's probably because you data model is flawed.
This is what your data model currently looks like:
This is what I believe it should look like:
It is critical to have your data model right before you start coding. It can save you many headaches!

Delete Multiple Selected Records from checkbox in a relation

I have two Model. One is user and onother is UserProfile and in created their tables users and user_profiles respectivly. I want when i delete user data thenrelated UserProfile should also automatically deleted. I created hasone and belongs to relation. One more thing i am deleting data using checkbox with delete button so i could able to delete multiple record at once.
User.php
public $hasOne = array(
'UserProfile' => array(
'className' => 'UserProfile',
'foreignKey' => 'user_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
UserProfile.php
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
);
In UsersController i have a deleteSelected function
UsersController.php
public function deleteSelected()
{
foreach($this->data['User'] as $key => $value)
{
if($value != 0)
{
$this->User->delete($value);
}
}
$this->redirect($this->referer());
}
finaaly i am getting an Error: Table groups_users for model GroupsUser was not found in datasource default. Please tell whats wrong in my code. Thanks.
set dependent to true
public $hasOne = array(
'UserProfile' => array(
'className' => 'UserProfile',
'foreignKey' => 'user_id',
'dependent' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);

Getting data from a $hasAndBelongsToMany relation at CakePHP 2.2

I have 3 tables:
- users
- posts
- Subscriptions (which should be by default posts_users (n-n))
The subscriptions table is the n-n table needed for this relation. I didnt want to call it "posts_users" as it is very confusing for my system because i have more relations between users and posts.
The, to create the hasAndBelongsToMany relation, i did this:
Post (model):
public $hasAndBelongsToMany = array(
'Subscriptions' => array(
'className' => 'User',
'joinTable' => 'subscriptions',
'foreignKey' => 'post_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
User (model):
public $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post',
'joinTable' => 'subscriptions',
'foreignKey' => 'user_id',
'associationForeignKey' => 'post_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Lets say i want to find the subscribed posts for the user with id 3.
Is there any way to do a find to retrieve data of subscribed Posts for a user?
In which model should i do the query? How??
Thanks.
Ok. Finally i got it.
You can do the query from any of the two related models. In this case Post or User.
$this->Post->User->find('all', array(
'conditions' => array('User.id' => $this->Session->read('Auth.User.id')),
'contain' => array('Subscriptions')
));
This would return the subscribed tickets (in the subscriptions array) for the current user.

How do I get the releated object names?

I have a problem that I've been trying to resolve for hours. Let's say that I have a book model, an author model and a country model.
When viewing a book entry, at the bottom of the page, there are the related objects. In this case, authors.
The page shows me the related data for the author, the author's name and other details display fine. The country, however, is displayed as country_id.
Is there a way that I can change that to country name?
UPDATE:
I have these three tables:
Book HMBM Authors H Country
A book may be written by many authors (for anthologies)
An author may write many books
An author may reside in a country
A country may host may authors.
Book:
public $hasAndBelongsToMany = array(
'Author' => array(
'className' => 'Author',
'joinTable' => 'author_books',
'foreignKey' => 'book_id',
'associationForeignKey' => 'author_id',
'unique' => '',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
Author:
public $hasMany = array(
'Book' => array(
'className' => 'Book',
'foreignKey' => 'book_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
Country:
public $hasMany = array(
'Author' => array(
'className' => 'Author',
'foreignKey' => 'country_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
If a book has an author, and an author has a country, the data you require is outside the ORM's mechanism to avoid recursive calls to the database. Call $this->Book->recursive = 2in your controller method to increase the recursion limit.

How to get only products with images in CakePhp

<?php
class Product extends AppModel {
var $name = 'Product';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Brand'
);
var $hasOne = array(
'PhotoSmall' => array(
'className' => 'Photo',
'foreignKey' => 'product_id',
'dependent' => true,
'conditions' => 'PhotoSmall.tipo = "small"',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
);
var $hasMany = array(
'PhotoBig' => array(
'className' => 'Photo',
'foreignKey' => 'product_id',
'dependent' => true,
'conditions' => 'PhotoBig.tipo = "big"',
'fields' => '',
'order' => 'PhotoBig.order',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'product_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
I need all my finds (except in the admin area) to retrieve only products with images.
In the case of PhotoSmall is easy, I can do PhotoSmall.id IS NOT NULL in a condition because CakePhp generates a left join, but i can not find a way to require a least on PhotoBig because CakePhp does two queries to return its results.
The requirement is the following, I can only show products with a PhotoSmall and a least one PhotoBig system wide (except for the admin section of the site), the solution should work with CakePhp pagination
I've tried the following code without success, it only returns product data, not photo data:
$this->Product->recursive = -1;
$conditions = array('Product.category_id'=> $cats);
$joins = array(
array('table' => 'photos',
'alias' => 'PhotoBig',
'type' => 'INNER',
'conditions' => array(
'Product.id = PhotoBig.product_id',
)
),
array('table' => 'photos',
'alias' => 'PhotoSmall',
'type' => 'INNER',
'conditions' => array(
'Product.id = PhotoBig.product_id',
)
)
);
$this->paginate = array(
'limit' => 12,
'conditions' => $conditions,
'joins' => $joins,
);
you need to additionally use contain - at least that worked for me
(due to your recursive=-1):
'contain' => array('PhotoSmall', 'PhotoBig'),
the joins are only the setup configuration. without specifying your data you will only get the one model's data.
do you use the containable behavior? would be the easiest solution.
if not, try to set recursive to 0 or 1.
PS: and your second alias is wrong inside the 'conditions' (should be PhotoSmall there).
Well firstly you're setting recursive to -1 which should just give you Product data no matter what kind of find you do.
Why don't you do an AND condition in a find on the Product model like so:
$this->Product->find('all', array('recursive' => 1, 'conditions' => array(
"NOT" => array('PhotoSmall.id' => NULL, 'PhotoBig.id' => NULL))));
Note: recursive will need to be at least one because you want related data.

Resources