cakeDC search on concat fields - cakephp

I am using the cakeDC search plugin for cakephp and I need to search by full name. I have stored the name in the database as separate first and last names. How would I concat the first and last name and then perform my search.
So my form input will search either member number or full name.
cakephp version 2.3.5
public $filterArgs = array(
'member_no' => array(
'type' => 'like' ,
'field' => array(
'member_no',
'name'
)
)
);
Update:
Here is what it would look like as a sql query:
SELECT
CONCAT_WS(' ', firstName,lastName) AS name
FROM
table
WHERE
name LIKE '%$keywords%'
Thanks to Mark here is what I added to the Model. All works great now.
public $virtualFields = array(
'name' => 'CONCAT(Member.first_name, " ", Member.last_name)'
);

If you just want the example above in a clean way why not using the plugin as documented? allowing it to do all the work?
public $virtualFields = array(
'full_name' => 'CONCAT_WS(' ', firstName,lastName)'
);
public $filterArgs = array(
'search' => array(
'type' => 'like' ,
'field' => 'full_name'
)
);
That's all there is to it. Or what is the issue?

Related

CakePhp: Find using Contain with condition

I have the following models:
Company(id, name)
Employee(id, name, company_id, isRemoved) [Company has many Employees]
In the association specified, the employee has a default condition, that
public $hasMany = array(
'Employee' => array(
'className' => 'Employee',
'foreignKey' => 'company_id',
'dependent' => true,
'conditions' => array(
'Employee.isRemoved' => 0
),
)
);
The association has a default condition of an employee being not removed. I am using the following Find Query on company to get only those employees whose name matches a string:
$this->Company->find('all', array(
'contain' => array(
'Employee' => array(
'conditions' => array(
'Employee.name LIKE' => '%'.$search_text.'%')
),
'fields' => array('Employee.id, Employee.name')
)
)
));
The problem I am facing is that, When I use conditions within contain, the default condition specified in the association is not applied and when the conditions key is not specified, the default condition specified in the association is applied.
Is this a default behaviour of Cakephp and How to proceed about it? I am using Cakephp 2.8.4
I can not tell you if the conditions in the model being overwritten is default behaviour of CakePHP. I can however offer you a possible alternative:
By using the beforeFind() callback in your model you could add your 'Employee.isRemoved' => 0 condition.
So in your Company model you could do something like:
function beforeFind(array $queryData) {
if(isset($queryData['contain']['Employee'])) {
//Notice the extra [] to not overwrite the conditions set in the controller
$queryData['contain']['Employee']['conditions'][]['Employee.isRemoved'] = 0;
}
return $queryData;
}
Disclaimer: I did not test this code.
Source: https://stackoverflow.com/a/17544106/6786476

HABTM Find with CakePHP 2.0

I am trying to do a search, using pagination for posts which have a specific tag or tags (for example, if a user was to select two tags, then posts containing either tag would be returned).
I have the relationship defined in my Posts table
public $hasAndBelongsToMany = array('Tags' => array(
'className' => 'Tags',
'joinTable' => 'posts_tags',
'foreignKey' => 'post_id',
'associationForeignKey' => 'tag_id',
'unique' => 'keepExisting'));
How do I use Find to retrieve rows with a given tag (name or ID would be fine)
Trying:
// other pagination settings goes here
$this->paginate['conditions']['Tags.id'] = 13;
gives me an error that the relationship does not exist.
Looking at the debug info it appears that the tables are not joining the Posts_Tags and Tags table, however, when I debug the data making it to the view, the Posts objects contain the tags data.
Most of the documentation I can find for this seems to revolve around earlier versions of CakePHP, any help would be appreciated.
Could not find a satisfying solution myself.
I created a behavior to take care of this.
Create a file called HabtmBehavior.php and put it in your app/Model/Behavior folder.
Put the block of code in there and save file.
Add the behavior to your model: eg public $actsAs = array('Habtm');
Here is a usage example with find.
<?php $this->Entry->find('all', array('habtm'=>array('Tag'=>array('Tag.title'=>'value to find'))) ?>
Paginate would look something like this:
$this->paginate['Entry']['habtm']['Tag'] = array('Tag.title'=>'value to find');
You are free to add as many relations as you want by adding additional Model Names in the habtm array.
(Just be careful not to make it to complex since this could start slowing down your find results.)
<?php
class HabtmBehavior extends ModelBehavior {
public function beforeFind(Model $model, $options) {
if (!isset($options['joins'])) {
$options['joins'] = array();
}
if (!isset($options['habtm'])) {
return $options;
}
$habtm = $options['habtm'];
unset($options['habtm']);
foreach($habtm as $m => $scope){
$assoc = $model->hasAndBelongsToMany[$m];
$bind = "{$assoc['with']}.{$assoc['foreignKey']} = {$model->alias}.{$model->primaryKey}";
$options['joins'][] = array(
'table' => $assoc['joinTable'],
'alias' => $assoc['with'],
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array($bind)
);
$bind = $m.'.'.$model->{$m}->primaryKey.' = ';
$bind .= "{$assoc['with']}.{$assoc['associationForeignKey']}";
$options['joins'][] = array(
'table' => $model->{$m}->table,
'alias' => $m,
'type' => 'inner',
'foreignKey' => false,
'conditions'=> array($bind) + (array)$scope,
);
}
return $options;
}
}
Hope this helps.
Happy baking.
I think the best solution is apply find function on join table Model. I try this before and it's work fine.
in your PostTag model :
/**
* #see Model::$actsAs
*/
public $actsAs = array(
'Containable',
);
/**
* #see Model::$belongsTo
*/
public $belongsTo = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id',
),
'Tags' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
),
);
in your controller :
// $tagsId = tags ids
$posts = $this->PostTag->find('all', array('conditions' => array('PostTag.tag_id' => $tagsId),'contain' => array('Post')));
also is better follow cake naming convention, if you have tags(plural), post_tags(first singular second plural),posts(plural) tables you must have Tag,PostTag,Post Models.

how can i concat first_name and last_name two different database fields to assign to drop down list in cakephp using 'list' in find statement

$swimmer = $this->Swimmer->find('list', array(
'conditions' => array('Swimmer.group' => $this->data['Swimmer']['group_id']),
'order' => array('Swimmer.first_name ASC'),
'fields' => 'Swimmer.first_name'
));
First of all, setup a virtual field in your Swimmer model as suggested by Ann Pham. eg:
var $virtualFields = array(
'name' => "CONCAT(Swimmer.first_name, ' ', Swimmer.last_name)"
);
Then, fetch the data for your dropdown list like so: (assuming Swimmers controller)
$this->Swimmer->find('list', array('fields' => array('Swimmer.id', 'Swimmer.name')));
You could also try doing this in your SwimmerModel: var $displayName = 'Swimmer.name'; (not 100% sure if this would work). If it does work, you won't need the 'fields' array in the find.
An another approach is to concat the field on the fly in the fields list.
Ex:
$swimmer = $this->Swimmer->find('list',
array(
'conditions' => array(
'Swimmer.group' => $this->data['Swimmer']['group_id']),
'order' => array(
'Swimmer.first_name ASC'
),
'fields' => 'CONCAT(Swimmer.first_name, " ", Swimmer.first_name) AS full_name'
));
http://book.cakephp.org/view/1588/virtualFields

CakePHP Model Relationship with Multiple Foreign Keys

In my CakePHP app I have models for Matches and Teams. Each Match has a home_team_id and an away_team_id, both of which reference a different Team.
In my team.php file, I am able to form the relationship for a Team's home matches:
var $hasMany = array(
'HomeMatch' => array('className' => 'Match', 'foreignKey' => 'home_team_id'),
'AwayMatch' => array('className' => 'Match', 'foreignKey' => 'away_team_id')
);
My problem is that I cannot automatically retrieve a Team's home and away Matches in a single array. That is, the retrieved Matches are returned in separate HomeMatch and AwayMatch arrays, which causes sorting difficulties.
I have tried the following:
var $hasMany = array(
'Match' => array('foreignKey' => array('home_team_id', 'away_team_id'))
);
...with no luck.
Any ideas on how to combine these two foreign keys into a single relationship?
Thanks, Ben
A custom finderQuery should do the trick:
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'finderQuery' => 'SELECT *
FROM `matches` as `Match`
WHERE `Match`.`home_team_id` = {$__cakeID__$}
OR `Match`.`away_team_id` = {$__cakeID__$}'
)
);
I was having a similar issue and instead of creating a finderQuery I used the conditions operator and it worked great!
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'conditions' => array(
'OR' => array(
array('Match.home_team_id' => '{$__cakeID__$}'),
array('Match.away_team_id' => '{$__cakeID__$}')
)
),
)
);
They are returned in seperate array's because the sort of represent different models (in this particular case the model is the same).
You should probably build a helper method to go over the retrieved data (in the model object or in a separate helper class) and "flatten" it. then you'd be able to sort it.
Ken.

cakephp find list

Hi I want to be able to generate a list using find so that I can use in select helper. but there is a problem. i want too fetch id,name(first + last). so how can I achieve it. I want first_name and last_name to be joined as name . How can I achieve it.
$this->User->find('all',array('fields' => array('first_name','last_name','id')));
I can't use model filters and callback Please suggest me how can I do it in controllers itself.
I think this can be done using the virtualFields and displayField properties in your model.
In your model, define a virtual field for the full name like this:
public $virtualFields = array(
'full_name' => 'CONCAT(User.first_name, " ", User.last_name)'
);
If you now set displayField to full_name you should be able to get a list of your users with the $this->User->find('list') method which you can use without problems with the Form-helper.
public $displayField = 'full_name';
... or:
public $displayField = 'User.full_name';
The id is fetched automatically.
Another solution is to use Cake's Set::combine to build what you need...
$users = $this->User->find('all',array('fields' => array('first_name','last_name','id')));
$user_list = Set::combine($users, '{n}.User.id', array('{0} {1}', '{n}.User.first_name', '{n}.User.last_name'));
Result will look something like:
array(
[2] => 'First Last',
[5] => 'Bob Jones'
)
Here's the documentation link:
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::combine
To achieve this first go to the model and add this line
public $virtualFields = array('full_name' => 'CONCAT(first_name, " ", last_name)');
and then go to controller file just use the name "full_name" which you put in virtual fields
$this->User->find('all',array('fields' => array('full_name','id')));
It returns name with combined fields
+1 on Tim's answer, however, if you need an alternative, teknoid wrote a nice article about this a long time ago:
http://nuts-and-bolts-of-cakephp.com/2008/09/04/findlist-with-three-or-combined-fields/
In my case, Set::combine was the way to go, since I had to deal with concatenation of fields in associated models, like:
$bancos_enteros = $this->Financiacion->Banco->find('all', array(
'fields' => array('Empresa.codigo_contable','Empresa.nombre_corto', 'Banco.id'),
'order' => array('Empresa.codigo_contable' => 'asc'),
'recursive' => 1
));
$bancos = Set::combine(
$bancos_enteros,
'{n}.Banco.id',
array(
'{0} {1}',
'{n}.Empresa.codigo_contable',
'{n}.Empresa.nombre_corto'
)
);
returning
array(
(int) 14 => '57200002 Caixa',
(int) 15 => '57200003 Sabadell',
(int) 3 => '57200005 BBVA',
(int) 16 => '57200006 Deutsche Bank',
(int) 17 => '57200007 Popular',
(int) 18 => '57200009 March',
(int) 26 => '57200010 Bankinter',
(int) 4 => '57200011 Santander'
)
While
$this->Financiacion->Banco->Empresa->virtualFields = array(
'codigo_nombre' => 'CONCAT(Empresa.codigo_contable,Empresa.nombre_corto)'
);
$this->Financiacion->Banco->virtualFields['codigo_nombre'] = $this->Financiacion->Banco->Empresa->virtualFields['codigo_nombre'];
$bancos = $this->Financiacion->Banco->find('list', array(
'fields' => array('Banco.id','Banco.codigo_nombre'),
'order' => array('Banco.codigo_nombre' => 'asc'),
'recursive' => 1
)
);
returns a SQL error in a following query if I don't delete the virtual fields first:
unset($this->Financiacion->Banco->Empresa->virtualFields);
unset($this->Financiacion->Banco->virtualFields);

Resources