cakephp3.0 pagination giving error for join table - mysql

I am using cakephp 3.0 and trying to pagination result set from two tables
$this->paginate = array(
'fields' => array(
'ModifierGroups.id',
'ModifierGroups.name',
'ModifierGroups.type',
'ModifierGroups.house_id',
'Houses.name',
),
'conditions' => $search_filter,
'limit' => 18,
'order' => ['ModifierGroups.name' => 'ASC'],
'recursive'=>-1,
);
$this->paginate('ModifierGroups')
but it's giving me this error
Column not found: 1054 Unknown column 'Houses.name' in 'field list'
ModelTable
$this->belongsTo('Houses', [
'foreignKey' => 'house_id'
]);

you should use contain to include 'Houses', associated models are no longer lazy loaded. recursive is also gone in CakePHP3
http://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#eager-loading-associations

Related

CakePHP 3 quoting function names defined in 'fields' configuration of my find() call

I am querying a database table conveniently named order and because of that I had to set 'quoteIdentifiers' => true in my app.php configuration. However, when I'm putting function names into the fields configuration of my find() call, CakePHP quotes them too.
$orders->find('all', array(
'fields' => array(
'DATE(Orders.date_added)',
'COUNT(*)',
),
'conditions' => array(
'Orders.order_status_id <>' => 0,
),
'group' => array(
'DATE(Orders.date_added)',
),
));
The query above ends up calling
SELECT <...>, `DATE(Orders.date_added)` FROM `order` <...>
which obviously throws an error.
I googled a lot, tried this:
$orders = $orders->find('all', array(
'conditions' => array(
'Orders.order_status_id <>' => 0,
),
'group' => array(
'DATE(Orders.date_added)',
),
))->select(function($exp) {
return $exp->count('*');
});
and that didn't work either, throwing me some array_combine error.
Is there any way for me to un-quote those function names, while keeping the rest of the query quoted automatically? Here's what I'm trying to accomplish:
SELECT <...>, DATE(Orders.date_added) FROM `order` <...>
Please help.
You should use function expressions, they will not be quoted, except for arguments that are explicitly being defined as identifiers:
$query = $orders->find();
$query
->select([
'date' => $query->func()->DATE([
'Orders.date_added' => 'identifier'
]),
'count' => $query->func()->count('*')
])
->where([
'Orders.order_status_id <>' => 0
])
->group([
$query->func()->DATE([
'Orders.date_added' => 'identifier'
])
]);
I'd generally suggest that you use expressions instead of passing raw SQL snippets wherever possible, it makes generating SQL more flexible, and more cross-schema compatible.
See also
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions

Linking two models with CakeDC Search Plugin

I have a small problem that is me consuming days.
I need made a search in two diferent models in CakePHP, i'm using CakeDC Search Plugin.The models are 'Desordens' and 'Sinonimos', the association is hasMany. One 'Desorden' hasMany 'Sinonymous':
public $hasMany = array(
'Sinonimo' => array(
'className' => 'Sinonimo',
'foreignKey' => 'desorden_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
So, i follow the example in CakeDC Docs an made this :
public $filterArgs = array(
'busca' => array(
'type' => 'like',
'encode' => true,
'before' => false,
'after' => false,
'field' => array(
'Desorden.name',
'Desorden.orphanumber',
'Sinonimo.sinonimo')
)
);
What i want to do is a search where the user can insert the name,orphanumber or the synonymous.In SQL would be:
SELECT `Desorden`.`id`, `Desorden`.`name`, `Desorden`.`orphanumber`, `Desorden`.`expertlink`, `Desorden`.`tipodesordem`, `Desorden`.`descricao`, `Sinonimo`.`id`, `Sinonimo`.`sinonimo`, `Sinonimo`.`desorden_id` FROM `rederaras`.`desordens` AS `Desorden` LEFT JOIN `rederaras`.`sinonimos` AS `Sinonimo` ON (`Sinonimo`.`desorden_id` = `Desorden`.`id`) WHERE ((`Desorden`.`name` LIKE 'mult') OR (`Desorden`.`orphanumber` LIKE 'mult') OR (`Sinonimo`.`sinonimo` LIKE 'mult'))
But the table 'Sinonimo' its not being joined and i have the 1054 error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Sinonimo.sinonimo' in 'where clause'
I don't know what i missing, the controller i have configured like this:
$this->Prg->commonProcess();
$this->Desorden->recursive = 0;
$this->Paginator->settings['conditions'] = $this->Desorden->parseCriteria($this->Prg->parsedParams());
$this->set('desordens', $this->Paginator->paginate());
I think its no problem in the model or cakedc filterArg configuration, because the query is constructed in the right way.I try to use bindModel to made a join between the tables 'Desordens' and 'Sinonimos' but no success....
$this->Desorden->bindModel(array(
'hasMany' => array(
'Sinonimo' => array(
'foreignKey' => false,
'conditions' => array('Desorden.id = Sinonimo.desorden_id')
)
)
), false);
Anyone can help me to made this join ?
Thanks
I solved the problem using $belongsTo in the two Models. When i set $belongsTo the CakeDC create a Join between the tables. I don't know if this a best solution, but works for me. If this help someone.

Complex CakePHP query refactoring

I have just inherited a project and I need to re-work a bit of code so that the pagination of an infinite scroller still work properly.
Right now the code is grabbing all of the categories and their products, and listings. I need to edit it so that only categories that have, products, which have active listings are returned.
Here is the code that eventually worked:
$catData = $this->find('all',array(
'conditions' => array(
'Indexer.type' => 'Category',
'Listing.listing_id IS NOT NULL'
),
'joins' => array(
array('table' => 'peeka_product_category_link',
'alias' => 'Link',
'type' => 'LEFT',
'conditions' => array(
'Link.category_id = Category.category_id'
)
),
array('table' => 'peeka_products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array(
'Product.product_id = Link.product_id'
)
),
array('table' => 'peeka_listings',
'alias' => 'Listing',
'type' => 'LEFT',
'conditions' => array(
'Listing.product_id = Product.product_id',
'Listing.listing_end_date >=' => $date,
'Listing.listing_start_date <=' => $date,
"Listing.listing_status = 'Active'"
)
),
),
'order' => 'Category.category_name ASC',
'limit' => $set_limit,
'fields' => array('Category.category_id, Category.category_name, Indexer.url'),
'group' => 'Category.category_id',
'recursive' => 0
));
EDIT: Thanks to Dave this is working now and I just wanted to post it for future reference. Maybe it will help someone else.
"...only categories that have, products, which have active listings are
returned."
"...a way to combine these three queries into one, so that the first
Category->find() function retrieves all the valid data."
To retrieve data and restrict it based on fields of associated models, you'll want to use CakePHP's JOIN.
It's hard to answer your question further than that without just writing the code for you, so - give JOINs a try, then come back and ask another more specific question if you have any issues with it.

Zend Framework 2 - Multiple sub domains cause problems

I am writing an application in Zend Framework 2 which is going to run from a few different subdomain, and I want to have a different module for each sub domain, to keep things tidy.
My problem is when I add more than 1 sub domain to the routing, it loses one of the sub domains.
eg: This setup works
testbed.localhost (module/Application)
a.testbed.localhost (module/A)
If I add an extra one it will the route all requests for a to the Application Index Controller
eg
testbed.localhost (module/Application), a.testbed.localhost (module/A), b.testbed.localhost (module/B)
This is the module.config.php for module/A
'router' => array(
'routes' => array(
'ads' => array(
'type' => 'Hostname',
'options' => array(
'route' => 'a.testbed.localhost',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'__NAMESPACE__' => 'A\Controller',
'controller' => 'A\Controller\A',
'action' => 'index',
),
),
And this is the route in module.config.php in module/B
'router' => array(
'routes' => array(
'ads' => array(
'type' => 'Hostname',
'options' => array(
'route' => 'b.testbed.localhost',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'__NAMESPACE__' => 'B\Controller',
'controller' => 'B\Controller\B',
'action' => 'index',
),
),
Now the namespaces are correct in both the module.config.php files, what I have noticed is the sub domain a.testbed.localhost will work if I remove the reference to it from config/application.config.php
<?php
return array(
'modules' => array(
'Application',
'A',
'B', <--- A doesn't work if B is here
),
And if I swap A & B around in the modules array above, then B will get forwarded to the Application Module and A will work. So it seems to have problems with more than 1 sub domain. Has anyone got any ideas / come across the same thing?
This happens because your route names are the same. I would try a-ads and b-ads for route names and that should resolve your situation.
In the end the configuration is getting merged together. So it's like an array, when the last array is merged it overwrites anything before it.

Joins - Merge result into single, multidimensional array

I'm performing a simple query that joins two tables together. What I get is something like this.
array(
[0] => array(
'id' => 52
'name' => 'charles',
'sale_id' => 921,
'sale_time' => 1306393996,
'sale_price' => 54.21
),
[1] => array(
'id' => 52
'name' => 'charles',
'sale_id' => 922,
'sale_time' => 1306395000,
'sale_price' => 32.41
),
...
);
...which is the expected result. However, I'd like the query to return something like this:
array(
[0] => array(
'id' => 52,
'name' => 'charles',
'sales' => array(
[0] => array(
'sale_id' => 921,
'sale_time' => 1306393996,
'sale_price' => 54.21
),
[1] => array(
'sale_id' => 922,
'sale_time' => 1306395000,
'sale_price' => 32.41
),
...
)
)
)
Now I realize I could simply perform two queries, one for the user info, and another for sales, and merge those arrays together using whatever language I'm using (PHP in this case). But I have many arrays of properties and querying and merging for those seems awfully inelegant to me (although it does work). It seems to me there'd be a way to work with a single, unified object without duplicating data.
Just wondering if there was a no-brainer query, or if that's simply not easy through MySQL alone.
I would say this is not possible with MySQL alone - you have to do some tricks at application level. That is, because even if you send a single query that will bring you all the data from MySQL to your application (PHP), they will come as a denormalized array of data - your first case.
If you want to get the data as in your second case, I'd recommend using some ORM - in Ruby there is ActiveRecord, in Perl there are Class::DBi, DBIx::Class and many more - I can not name one for PHP that is able to do this, but I am sure there are plenty.