Cakephp pagination with joined tables - sql

I want to join the "sectors" table to my pagination results. Here is what happens:
I set the paginate variable so that it will join Sector:
$this->paginate = array(
'joins' => array(
array(
'table' => 'sectors',
'alias' => 'Sector',
'type' => 'left',
'foreignKey' => false,
'conditions' => array('Company.sector_id = Sector.id'))),
'conditions' => $conditions);
and then go
$jobs = $this->paginate('Job');
The query generated by the paginator is corrupted because it joins Sector before Company.
Here is the part of the sql output:
SELECT COUNT(*) AS `count` FROM `jobs` AS `Job` left JOIN sectors AS `Sector` ON (`Company`.`sector_id` = 'Sector.id') LEFT JOIN `companies` AS `Company` ON (`Job`.`company_id` = `Company`.`id`)
and it should be:
SELECT COUNT(*) AS `count` FROM `jobs` AS `Job` LEFT JOIN `companies` AS `Company` ON (`Job`.`company_id` = `Company`.`id`) left JOIN sectors AS `Sector` ON (`Company`.`sector_id` = 'Sector.id')
I paginate the Job model and it is related to the Company model but not the Sector model. Thats why I need to join the Sector with pagination.
How can I fix this?

'Joins' conditions in CakePHP should not be specified as an associative array, but as a string, otherwise CakePHP will handle 'Sector.id' as a literal string not as a reference to another Model/field.
So, replace this:
'conditions' => array('Company.sector_id' => 'Sector.id')
With this
'conditions' => array('Company.sector_id = Sector.id')
And your query should be ok

My solution is to join the Job model with the Sector.

Related

Converting SQL query to CakePHP

I have this SQL query that I need to convert to CakePHP. I used this website [http://dogmatic69.com/sql-to-cakephp-find-converter][1] that converts the code but I doesn't work in the way that I want. There is no results.
I think it is because it creates an empty array
here is the SQL code :
SELECT shops.phone1 FROM galleries , albums , shops
WHERE galleries.album_id = albums.id and albums.shop_id = shops.id and galleries.id = 210
and this is the result the website gives me :
$options = array(
'fields' => array(
'shops.phone1',
),
'joins' => array(
array(
),
),
'conditions' => array(
'Gallery.album_id = albums.id',
'albums.shop_id = shops.id',
'Gallery.id' => '210',
),
);
$data = $this->find('all', $options);
but the code doesn't work.
Any Ideas what could be wrong ?
Thanks
There are many ways to achieve this.
If you have defined your associations correctly then you can do this:
$data = $this->Shops->find('all')
->contain(['Galleries','Albums'])
->fields(['Shops.phone1'])
->where(['Galleries.id' => 210]);
Otherwise you can use custom join to generate your query:
$data = $this->Shops->find('all')
->join([
'albums' => [
'table' => 'albums',
'type' => 'INNER', // define your join type here
'conditions' => 'albums.shop_id = Shops.id',
],
'galleries' => [
'table' => 'galleries',
'type' => 'INNER', // define your join type here
'conditions' => 'galleries.album_id=albums.id',
]
])
->select(['Shops.phone1'])
->where(['galleries.id' => 210]);
Further Reading: Cakephp -> Query Builder -> Adding Joins

CActiveDataProvider Querying on join

I have a simple SQL query, which I need to convert to use in Yii 1.1.
SELECT *
FROM User
INNER JOIN Role ON Role.UserId = User.Id
WHERE Role.Name = 'admin'
How is this written into the CActiveDataProvider?
I have came up with an answer. Hopefully it helps someone in the future.
$dataProvider = new ActiveDataProvider('User', array
(
'criteria' => array
(
'with' =>'roles',
'join' => 'INNER JOIN Role r ON r.UserId = User.Id',
'condition' => 'r.Name=:term',
'params' => array(':term'=>'admin')
)
));

Cakephp: Group by foreign key id and order by published date

public function index() {
$this->paginate = array(
'order'=>array('published_date'=>'desc'),
'group'=>'book_id',
'conditions'=>array('Chapter.published'=>1,'Chapter.published_date <= NOW()')
);
$chapters = $this->paginate();
$this->set('chapters',$chapters);
}
My database have table Book, Chapter. Each book has many chapter
Table Chapter has field '*book_id*', that is foreign key reference table Book primary key (id).
Here is my index page. The idea is to get latest chapter from each book and order by published date.In controller ChapterController, I use group by statement but it didn't get the latest chapter, it get the first chapter form each book.
So,please help me fix it. Thanks a lot
I believe that group by will use the first row that is naturally in your results, and I do not think there is a simple way to sort your results before the group by is applied. But I think the following query will get you the results you want:
SELECT * FROM books INNER JOIN (SELECT * FROM chapters WHERE chapters.published = 1 AND chapters.published_date <= NOW() ORDER BY chapters.published_date DESC) AS Chapter ON Chapter.book_id = books.id GROUP BY books.id
I think the following is how the CakePHP should look (I'm assuming you're paginating Book):
$this->Book->recursive = -1; //Join manually instead
$this->paginate = array(
'order' => array('published_date' => 'desc'),
'group' => 'Book.id',
'joins' => array(
array(
'table' => '(SELECT * FROM chapters)',
'alias' => 'Chapter',
'conditions' => array(
'Chapter.book_id = Book.id',
'Chapter.published' => 1,
'Chapter.published_date <= NOW()',
),
'type' => 'inner',
'order' => array('published_date' => 'desc'),
),
),
);

converting sql query into cakephp format

Hi
i need the following sql query into cakephp find() format. the query it self is working fine but i need to change it.
$this->Part->query(
"SELECT `parts`.`id`,`parts`.`part_name`
FROM `parts`
LEFT JOIN (
SELECT `op` . *
FROM `order_parts` AS `op`
WHERE `op`.`order_id` =".$this->Session->read('orderid')."
) AS `vT`
ON ( `parts`.`id` = `vT`.`part_id` )
WHERE `vT`.`part_id` IS NULL"
);
thanks
If your relationship are Order HABTM Part and you have a table orders_parts with columns: id, order_id,part_id you should be able to do something like this:
First, get the ids of the parts which are in the order:
//each Part has one OrdersPart per order
$this->Part->bindModel(array('hasOne' => array('OrdersParts')));
$parts = $this->Part->find('list', array(
'fields' => array('Part.name'),
'conditions' => array(
'OrdersParts.order_id' => $this->Session->read('orderid'),
),
'recursive' => 2
));
Now get the parts which are not in the order:
$this->Part->find('all', array(
'conditions' => array(
"NOT" => array('Part.id' => array_keys($parts))
),
));

is this a really bad way to do queries across multiple HABTM models?

I am trying to query across two HABTM tables. I have Leases Managers Tenants Properties.
Managers HABTM Tenants
Managers HABTM Properties
Tenants HABTM Leases
What I want to do is find a list of Properties linked to Managers linked to Tenant. I have been able to accomplish the query with the code below BUT I am only able to retrieve Property_id (from the Managers_Property model) and not Property.name (from the Property model).
I get the nagging feeling I am doing something very wrong or unnecessary here but I've been banging my head against the wall and haven't been able to figure this out.
$conditionsSubQuery['`ManagersTenant`.`tenant_id`'] = $this->Auth->User('id');
$dbo = $this->Lease->getDataSource();
$subQuery = $dbo->buildStatement(
// SELECT `ManagersTenant`.`manager_id` FROM `managers_tenants` AS `ManagersTenant` WHERE `ManagersTenant`.`tenant_id` = $this->Auth->User('id')
array(
'fields' => array('`ManagersTenant`.`manager_id`'),
'table' => $dbo->fullTableName($this->Lease->LeasesManager->Manager->ManagersTenant),
//'table' => $dbo->fullTableName($this->Lease->Property->ManagersProperty->Manager->ManagersTenant),
'alias' => 'ManagersTenant',
'limit' => null,
'offset' => null,
'joins' => array(),
'conditions' => $conditionsSubQuery,
'order' => null,
'group' => null
),
$this->Lease->LeasesManager->Manager->ManagersTenant
//$this->Lease->Property->ManagersProperty->Manager->ManagersTenant
);
$subQuery = ' `ManagersProperty`.`manager_id` IN (' . $subQuery . ') ';
$subQueryExpression = $dbo->expression($subQuery);
$conditions[] = $subQueryExpression;
$properties = $this->Lease->Property->ManagersProperty->find('list', array(
'conditions' => $conditions,
'fields' => array('property_id',),
'recursive' => 3
)
);
Any help is much appreciated.
Seems like you should be using the Containable behavior: http://book.cakephp.org/view/1323/Containable
$contain = array(
'Manager' => array(
'Property'
)
);
$conditions = array('Tenant.id' => $this->Auth->user('id'));
$tenants = $this->Tenant->find('first', array('conditions'=>$conditions, 'contain'=>$contain));