Cakephp 3 search query, with concat - cakephp

I'm trying to perform a search on two joined columns, fname and lname.
This does not appear to be working:
Object of class Cake\Database\Expression\FunctionExpression could not be converted to string
$users = $this->Users->find();
$users->select(['id', 'fname', 'lname'])
->where([$users->func()->concat(['fname', 'lname']).' LIKE' => $terms]);
Any ideas?
Thanks.

If you're concatenating first and last names, they should be concatenated with spaces in between.
$users->select(['id', 'fname', 'lname'])
->where(function ($exp, $query) use ($terms) {
$conc = $query->func()->concat([
'fname' => 'identifier', ' ',
'lname' => 'identifier'
]);
return $exp->like($conc, $terms);
});

Yu have to use query expression but this can't be done in a pagination array.
So Following ndn suggestion here's how I would do
create a custom finder. In your UsersTable file
public function findByKeyword(Query $query, array $options)
{
$keyword = $options['keyword'];
$query->where(
function ($exp, $q) use($keyword){
$conc = $q->func()->concat([
'Users.fname' => 'literal', รน
'Users.lname' => 'literal']);
return $exp
->or_([
'Users.fname LIKE' => "%$keyword%",
'Users.lname LIKE' => "%$keyword%",
])
->like($conc, "%$keyword%");
}
);
return $query;
}
Controller
$this->paginate = [
'finder' => [
'byKeyword' => [
'keyword' => $this->request->data['Users']['keyword']
]],
'conditions' => $limo, // this will merge your $limo conditions
// with the ones you set in the custom finder
'order'= > ['Users.fname desc'],
];
$this->set('userlist', $this->paginate($this->Users));

I think you have to use SQL expressions. Try something like this:
->where(function ($exp, $q) use($terms) {
$concat = $q->func()->concat([
'fname' => 'identifier',
'lname' => 'identifier'
]);
return $exp->like($concat, $terms);
});

Related

How to create subtraction with expression objects?

How can I get the proper result for TotalQt?
$query = $this->ItemLedgers
->find()
->where([
'ItemLedgers.processed_on <=' => date("Y-m-d",strtotime($to_date))
]);
$totalInCase = $query->newExpr()
->addCase(
$query->newExpr()->add(['in_out' => 'In']),
$query->newExpr()->add(['quantity']),
'integer'
);
$totalOutCase = $query->newExpr()
->addCase(
$query->newExpr()->add(['in_out' => 'Out']),
$query->newExpr()->add(['quantity']),
'integer'
);
$query
->select([
'total_in' => $query->func()->sum($totalInCase),
'total_out' => $query->func()->sum($totalOutCase),
'id',
'item_id',
'TotalQt' => $query->func()->sum($totalInCase) - $query->func()->sum($totalOutCase),
])
->contain(['Items' => function ($q) use($where) {
return $q
->where($where)
->contain(['Units']);
}])
->where(['company_id' => $st_company_id])
->group('item_id')
->autoFields(true)
->where($where);

Array to DB::insert()->values(), which is in different order with the columns

Hi folks! I'm trying to transfer data as array from the controller to the model, and then paste the data into the query builder, but the data must be in the same order as specified in the columns.
What options do I have?
And do you think this is a bad practice?
Controller:
$responseNotes = Model::factory('Notes')-> createTicket([
'description' => htmlspecialchars($_POST['description']),
'contact_id' => $_POST['contact_id'],
'pref_contact' => $_POST['pref_contact'],
'dog_id' => $_POST['document_id'],
'type' => $_POST['type'],
'owner_id' => Auth::instance()->get_user()->id,
'cc' => $_POST['cc-emails'],
'title' => $_POST['title']
]);
Model:
public function createNote(array $data)
{
$columns = [
'type',
'owner_id',
'cc',
'title',
'description',
'contact_id',
'pref_contact',
'dog_id'
];
if (!array_diff($columns, array_keys($data))) {
// All needed values exists
$result = DB::insert($this->NOTES, $columns)-> values($data)-> execute($this->SCHEMA);
}
return ($result) ? $result : false ;
}
Thanks to this answer. Solved this by:
// Order $data array according to $column.values
$orderedData = [];
foreach ($columns as $key) {
$orderedData[$key] = $data[$key];
}
$result = DB::insert($this->TICKETS, $columns)
-> values($orderedData)
-> execute($this->SCHEMA);
Why you don't use ORM Model?
in controller:
$responseNotes = ORM::factory('Notes')-> values([
'description' => htmlspecialchars($_POST['description']),
'contact_id' => $_POST['contact_id'],
'pref_contact' => $_POST['pref_contact'],
'dog_id' => $_POST['document_id'],
'type' => $_POST['type'],
'owner_id' => Auth::instance()->get_user()->id,
'cc' => $_POST['cc-emails'],
'title' => $_POST['title']
])
try{
$responseNotes->save();
} catch (ORM_Validation_Exception $ex) {
print_r($ex->errors('models'));
}
And don't use htmlspecialchars($_POST['description'])
In model class modify function (doc):
public function filters()
{
return array(
'description' => array( array('htmlspecialchars') ),
);
}
It looks like You have associative array with structure db_column=>value right? Than You can simply insert like this:
DB::Insert('table_name',array_keys($data))->values(array_values($data))->execute();

Count in contain Cakephp 3

I have a table Post and this has a has-many association with a table Stars.
I can get all the associated data using:
$this->Posts->find()->contain(['Stars']);
That works well.
But I want to count the Stars. I have tried this but its not working:
$this->Posts->find->contain([
'Stars' => function($q) {
return $q->select(['total' => $q->func()->count('Stars.post_id')]);
}
]);
//I've also tried this
...
...$q->select(['total' => "COUNT(Stars.post_id)"]);
...
//Also fail
This does not return the number of associated Stars.
Is there something wrong or should do it some other way?
Thanks
you have to select also the foreign key otherwise cake is not able to join the tables. And you have also to group the result
'Stars' => function($q) {
$q->select([
'Stars.post_id',
'total' => $q->func()->count('Stars.post_id')
])
->group(['Stars.post_id']);
return $q;
}
As here we have used total as virtual field, can be create more like this in same model as:
public function index()
{
$checklist = TableRegistry::get('Checklists');
$query = $checklist->find()
->where('Checklists.is_deleted = 0')
->contain([
'ChecklistTitles' => function($q) {
return $q -> select([
'ChecklistTitles.title',
'ChecklistTitles.checklist_id'
]);
},
'ChecklistTypes' => function($w){
return $w->select(['ChecklistTypes.type']);
},
'AssignedChecklists' => function($e){
$e->select([
'AssignedChecklists.checklist_id',
'completed' => $e->func()
->count('AssignedChecklists.checklist_id'),
])
->group(['AssignedChecklists.checklist_id'])
->where(['AssignedChecklists.is_deleted = 0 AND AssignedChecklists.checklist_status = 2']);
return $e;
}
]);
// ->toArray();
// pr($query);die;
$this->paginate = [
'limit' => 20,
'sortWhitelist' => [
'id', 'checklist_title', 'checklist_type'
]
];
$this->set('query', $this->paginate($query));
$this->set(compact('checklists','query'));
$this->set('_serialize', ['checklists','query']);
}
As here I have calculated completed, I want to calculate cancelled with different where condition, what will be the syntax for it in cakephp3?
Try this:
$total = $this->Posts->find()->contain(['Stars'])->count();
As refereed in the cookbook.

cakephp cakedc search plugin filter by name value and conditions

I have a cakedc search plugin with cakephp 3.0 working fine, but would like to have more advanced search filters like:
city = 'Los Angeles';
city != 'Los Angeles';
city LIKE '%Angeles%';
city LIKE 'Los%';
city NOT LIKE '%Angeles%';
city NOT LIKE 'Los%';
etc...
So i'm looking to add 2 dropdown select and 1 text input to achieve this.
'city' would be in a dropdown of db fields.
=, !=, like %?%, like %?, not like ?% conditions would be a dropdown
'los angeles' search value would be typed in.
Figured out, might not be perfect but seems to work fine:
View:
<?php echo $this->Form->input('dbfield', ['label' => 'Field', 'options' => ['' => '', 'region' => 'Region', 'city' => 'City', 'keyword' => 'Keyword']]); ?>
<?php echo $this->Form->input('dbconditions', [
'label' => 'Condition',
'options' => [
'' => '',
'contains' => 'contains',
'doesnotcontain' => 'does not contain',
'beginswith' => 'begins with',
'endswith' => 'ends with',
'isequalto' => 'is equal to',
'isnotequalto' => 'is not equal to',
],
]); ?>
<?php echo $this->Form->input('dbvalue', ['label' => 'Value', 'class' => 'form-control input-sm']); ?>
Model
public $filterArgs = [
'dbfield' => [
'type' => 'finder',
'finder' => 'conds0',
],
'dbconditions' => [
'type' => 'finder',
'finder' => 'conds0',
],
'dbvalue' => [
'type' => 'finder',
'finder' => 'conds',
],
];
public function findConds0(Query $query, $options = []) {
}
public function findConds(Query $query, $options = []) {
if($options['data']['dbconditions'] == 'contains') {
$conditions = [
$options['data']['dbfield'] . ' LIKE' => '%' . $options['data']['dbvalue'] . '%',
];
}
if($options['data']['dbconditions'] == 'doesnotcontain') {
$conditions = [
$options['data']['dbfield'] . ' NOT LIKE' => '%' . $options['data']['dbvalue'] . '%',
];
}
if($options['data']['dbconditions'] == 'beginswith') {
$conditions = [
$options['data']['dbfield'] . ' LIKE' => $options['data']['dbvalue'] . '%',
];
}
if($options['data']['dbconditions'] == 'endswith') {
$conditions = [
$options['data']['dbfield'] . ' LIKE' => '%' . $options['data']['dbvalue'],
];
}
if($options['data']['dbconditions'] == 'isequalto') {
$conditions = [
$options['data']['dbfield'] => $options['data']['dbvalue'],
];
}
if($options['data']['dbconditions'] == 'isnotequalto') {
$conditions = [
$options['data']['dbfield'] . ' != ' => $options['data']['dbvalue'],
];
}
return $query->where($conditions);
}
I clean the code, only beacuse look a big code.
In other way, why you include an empty option in the Condition select input? not look better with a default search condition?
Saludos

Cakephp 3 how to add beforeFind on table

I am reading the documentation and there seems to be no example of how to use the beforeFind in Cakephp 3. I thought maybe I could do it like I did it in 2, but that did not work. Here are the two ways I did it.
public function beforeFind(Event $event, Query $query, array $options, $primary){
$primary = (bool) $primary;
if(!empty($options['pure'])) {
$query->hydrate(false)
->join([
'table' => 'main_'.$this->store_id,
'alias' => 'c',
'type' => 'LEFT',
'conditions' => 'c.id = Stores.MAIN_ID',
]);
}
}
Second way:
public function initialize(array $config) {
if(!isset($config['store_id'])) throw new Exception('You must provide a store id');
$this->store_id = $config['store_id'];
$this->entityClass('P1\Model\Entity\Store');
$this->eventManager()->attach([$this,'addMain'],'beforeFind');
}
public function addMain(Event $event, Query $query, array $options, $primary){
$primary = (bool) $primary;
if(!empty($options['pure'])) {
$query->hydrate(false)
->join([
'table' => 'main_'.$this->store_id,
'alias' => 'c',
'type' => 'LEFT',
'conditions' => 'c.id = Stores.MAIN_ID',
]);
}
}
What am I doing wrong?
You must use method applyOptions from class Query.
$this->Posts->find()->applyOptions(['pure'=>true])->all()

Resources