How to modify join conditions CakePHP - cakephp

That is my Query object dump from debug...
I want to add more condition in method JOIN clause or i want setting default condition when execute joins by CakePHP.
My code:
public function beforeFind(Event $event, Query $query)
{
if ($query->join()) {
foreach ($query->join() as $key => &$join) {
$join['conditions'] = new QueryExpression('"aaaa" = "bbbbb"');
}
return $query;
}
}

You can check CakePHP Docs.
I believe you searching something like this:
class CustomerTable extends Table
{
public function initialize(array $config)
{
$this->hasOne('User')
->setName('User')
->setConditions(['User.active' => '1']) // or any other additional clause
->setDependent(true);
}
}
I whould sugggest you to use innerJoinWith, if you need to hook your normal association behaviour.
As a hardcode solution (that what I cann see from your code samples), read join method docs:
$query->join([
'user' => [
'table' => 'user',
'type' => 'INNER',
'conditions' => '"aaaa" = "bbbbb"'
]
]);
Now some telepathy,.. abracadabra,... wait-wait,... try this:
public function beforeFind(Event $event, Query $query)
{
$joins = $query->join();
if ($joins) {
foreach ($joins as $key => &$join) {
if ($key == 'user')
$join['conditions'] = ($join['conditions'] ? ' AND ' : '') . ' "aaaa" = "bbbbb"';
}
return $joins ? $query->join($joins) : $query;
}
}

Related

I am trying to paginate the search results and display it on the template's view file using CakePHP 3.x

This is the controller file where the all the search results are coming from the foreach loop , I tried paginating it, but can't figure out how to do it.
<?php
class SerController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('Paginator');
}
public function index()
{
$check_string=$this->request->data('check');
$check_break=explode(" ",$check_string);
$s1=TableRegistry::get('ser_tab');
foreach($check_break as $check)
{
$s11=$s1->find('all')->where(['OR' =>['text LIKE' => '%'.$check.'%','name LIKE' => '%'.$check.'%']]);
$this->set(array('data' => $s11));
}
$this->render('index');
}
}
Your query will take too long execution time. You can do it simply.
Update your index() function like as below
Example [When data from URL]
public function index($data='Default text here'){ /*When get Data from the URL*/
$data = explode(' ', $data);
$conditions = array(); /*All OR conditions will store here*/
foreach ($data as $key => $value) {
$conditions['or'][] = array('text LIKE' => "%$value%");
$conditions['or'][] = array('name LIKE' => "%$value%");
}
$this->paginate = [
'limit' => 25, #Set the limit of post
'conditions'=>[
$conditions /*OR conditions array*/
]
];
$data = $this->paginate();
var_dump($conditions,$data); /*Debug OR Condition array and Query data*/
$this->set(compact('data')); /*Set the Posts to a variable*/
}
Example [When data from POST]
public function index(){
if ($this->request->is('post')) {
$data = $this->request->data('check');
# YOUR CODE HERE
}
}
CakePHP 3.x pagination documentation is HERE

CakePHP 3 : Unknown method

I am creating a function in model to find all related services.
function in ServiceCategory.php
class ServiceCategory extends Entity
{
public function relatedServices($id)
{
return $this->find('all', [
'conditions' => [
'where' => [
'id !=' => $id
],
'limit' => 5
]
]);
}
}
And calling in ServiceCategoriesController.php
public function view($id = null)
{
$serviceCategory = $this->ServiceCategories->get($id, [
'contain' => ['Services']
]);
$relatedServices = $this->ServiceCategories->relatedServices($id);
$this->set('serviceCategory', $serviceCategory);
$this->set('relatedServices', $relatedServices);
$this->set('_serialize', ['serviceCategory']);
}
But it gives Unknown method 'relatedServices'
Is there any thing wrong I am doing ?
The code is in the wrong class
In the question:
class ServiceCategory extends Entity
This is an entity class
$relatedServices = $this->ServiceCategories->relatedServices($id);
This is making a call on a table object, table objects and entities do not inherit from each other, the method is unavailable to the table class.
Move the code to the table class
The direct solution is to move the code to the table class:
// src/Model/Table/ServiceCategoriesTable.php
namespace App\Model\Table;
class ServiceCategoriesTable extends Table
{
public function relatedServices($id)
{
return $this->find('all', [
'conditions' => [
'where' => [
'id !=' => $id
],
'limit' => 5
]
]);
}
Though the arguably correct/better way to do that is to implement a finder:
// src/Model/Table/ServiceCategoriesTable.php
namespace App\Model\Table;
use Cake\ORM\Query;
use \InvalidArgumentException;
class ServiceCategoriesTable extends Table
{
public function findRelatedServices(Query $query, array $options)
{
if (!isset($options['id'])) {
$message = sprintf('No id in options: %s', json_encode($options));
throw new InvalidArgumentException($message);
}
$query->where(['id !=' => $options['id']);
return $query;
}
Which would be called in exactly the same way as other find calls:
$relatedServices = $this->ServiceCategories->find(
'relatedServices',
['id' => $id]
);

Saving associated models in Cakephp 3

I have a form that collects data about an Article, and I want to save that data, as well as for a model called Abstract, where an Article hasMany Abstracts. My models look like this:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class AbstractsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Articles');
}
public function validationDefault(Validator $validator)
{
$validator
->notEmpty('body');
return $validator;
}
}
And
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->hasMany('Abstracts');
}
public function validationDefault(Validator $validator)
{
$validator ->notEmpty('category')
return $validator;
}
}
My input form has a field named 'abstracts.body', and in my ArticlesController I have this function:
public function add()
{
$data = $this->request->data;
$article = $this->Articles->newEntity($data, [
'associated' => ['Abstracts']
]);
if ($this->request->is('post')) {
$article->user_id = $this->Auth->user('id');
$data['abstracts']['user_id'] = $article->user_id;
$data['abstracts']['approved'] = 0;
$article = $this->Articles->patchEntity($article, $data, [
'associated' => ['Abstracts']
]);
if ($this->Articles->save($article, [ 'validate' => false,
'associated' => ['Abstracts']
]) )
{
$this->Flash->success(__('Your article has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your article.'));
}
$this->set('article', $article);
}
My Abstracts table is pretty straightforward:
CREATE TABLE 'abstracts' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'article_id' INTEGER , 'user_id' INTEGER , 'body' TEXT, 'approved' BOOLEAN )
From debugging I can see that I have the correct 'abstracts' array within my $data (in add()), but it doesn't appear to ever try to save it to the database. Can someone please point out my error? Thanks!
Got it.
I started going wrong here:
My input form has a field named 'abstracts.body'
Because it's a hasMany relationship, I need to have that input be 'abstracts.0.body'
Then the rest of LeWestopher's answer will work-- adding an index to the fields I want to fill in from the Controller, so $data[abstracts][0]['user_id'] => ... and so on. Thanks!
You're post processing your $data['abstracts'] array incorrectly resulting in the association not saving. $data['abstracts'] is expected to be an array of Abstracts. Your issue lies here:
$data['abstracts']['user_id'] = $article->user_id;
$data['abstracts']['approved'] = 0;
You should be able to fix this pretty easily by changing this to:
foreach($data['abstracts'] as $index => $abstract) {
$abstract['user_id'] = $article->user_id;
$abstract['approved'] = 0;
$data['abstracts'][$index] = $abstract;
}
This should correctly iterate over your array of abstracts, set the user_id and approved keys appropriately and then it should save correctly.
CakePHP 3.x Documentation on Saving Associations
EDIT: Very interesting issue indeed. Try it without using patchEntity, and use newEntity by itself instead:
public function add()
{
if ($this->request->is('post')) {
$data = $this->request->data;
// Post process abstracts objects
foreach($data['abstracts'] as $index => $abstract) {
$abstract['user_id'] = $article->user_id;
$abstract['approved'] = 0;
$data['abstracts'][$index] = $abstract;
}
// Build newEntity
$article = $this->Articles->newEntity($data, [
'associated' => ['Abstracts']
]);
// Save our entity with associations
if ($this->Articles->save($article, [
'validate' => false,
'associated' => ['Abstracts']
])) {
$this->Flash->success(__('Your article has been saved.'));
return $this->redirect(['action' => 'index']);
}
// On save fail
$this->Flash->error(__('Unable to add your article.'));
$this->set('article', $article);
}
}
EDIT 2: Your issue looks like it's definitely in your form helper. Your current form helper input creates an $data array that looks like this:
$data = [
'abstracts' => [
'body' => 'example text'
],
'category' => 'Science'
];
Which SHOULD look like:
$data = [
'abstracts' => [
['body' => 'example text'],
['body' => 'Im your second abstract'],
['body' => 'Abstract three!']
],
'category' => 'Science'
];
The issue lies in:
abstracts.body
Which should read as (in array dot notation):
// abstracts.0.body
echo $this->Form->input('abstracts.0.body', [
'label' => 'summary of article',
'maxlength' =>'440',
'rows' => '7'
]);
I believe that should be the last issue you run into.

How to use custom MySQL queries in CakePHP?

I am working on cakephp now.Please explain the process of custom mysql query in cake php.
we have to write query by using
$this->Model->query();
and I return this to controller.In controller,i loaded the model in particular function and i called the function and set that function to view like this
$this->set('post',$this->User->get());
is it correct process?please explain the code ...
What query do you want to write this way? It is possible to write nearly all queries using the CakePHP ORM. Using the query building functions of CakePHP is the prefered way of doing it.
All data fetching an manipulation should be done in a model as well. See Separation of Concerns.
Here is a complete model method to fetch an user record based on its id OR slug.
public function view($userId, $options = []) {
$defaults = array(
'contain' => array(),
'conditions' => array(
'OR' => array(
$this->alias . '.' . $this->primaryKey => $userId,
$this->alias . '.slug' => $userId,
),
$this->alias . '.email_verified' => 1
),
);
$result = $this->find('first', Hash::merge($defaults, $options));
if (empty($result)) {
throw new NotFoundException(__d('user_tools', 'User not found!'));
}
return $result;
}
Controller:
public function view($userId = null) {
$this->set('user', $this->User->view($userId);
}
Alternative but NOT preferred mode method to fetch te data
public function view($userId, $options = []) {
$result = $this->query(/* Your query here */);
if (empty($result)) {
throw new NotFoundException(__d('user_tools', 'User not found!'));
}
return $result;
}
Your solution is correct, let me eleborate it in more detail
1st Solution
In your controller
$arrayTemp =array();
$arrayTemp = $this->ModelName->query(' Your Query String ');
$this->set('post',$arrayTemp);
2nd Solution
In your model class
function returnDate(){
return $this->query('Your Query String')
}
In Controller class
$arrayTemp = $this->ModelName->returnDate();
$this->set('post',$arrayTemp);
}

CakePHP Pagination: how can I sort by multiple columns to achieve "sticky" functionality?

I see that this paginate can't sort two columns at the same time ticket is still open, which leads me to believe that what I'm trying to do is not possible without a workaround. So I guess what I'm looking for is a workaround.
I'm trying to do what many message boards do: have a "sticky" function. I'd like to make it so that no matter which table header link the user clicks on to sort, my model's "sticky" field is always the first thing sorted, followed by whatever column the user clicked on. I know that you can set $this->paginate['Model']['order'] to whatever you want, so you could hack it to put the "sticky" field first and the user's chosen column second. The problem with this method is that pagination doesn't behave properly after you do it. The table header links don't work right and switching pages doesn't work right either. Is there some other workaround?
User ten1 on the CakePHP IRC channel helped me find the solution. I told him that if he posted the answer here then I would mark it as the correct one, but he said I should do it myself since he doesn't have a Stack Overflow account yet.
The trick is to inject the "sticky" field into the query's "order" setting using the model's "beforeFind" callback method, like this:
public function beforeFind($queryData) {
$sticky = array('Model.sticky' => 'DESC');
if (is_array($queryData['order'][0])) {
$queryData['order'][0] = $sticky + $queryData['order'][0];
}
else {
$queryData['order'][0] = $sticky;
}
return $queryData;
}
What you can do is code it in the action. Just create the query you want when some parameters exist on the URL. (parameters has to be sent by GET)
For example:
public function posts(){
$optional= array();
if(!empty($this->params->query['status'])){
if(strlower($this->params->query['status']=='des')){
$optional= array('Post.status DESC');
}
else if(strlower($this->params->query['status']=='asc')){
$optional= array('Post.status ASC');
}
}
if(!empty($this->params->query['department'])){
//same...
}
//order first by the sticky field and then by the optional parameters.
$order = array('Post.stickyField DESC') + $optional;
$this->paginate = array(
'conditions' => $conditions,
'order' => $order,
'paramType' => 'querystring',
);
$this->set('posts', $this->paginate('Post'));
}
I have used something similar to filter some data using $conditions instead of $order and it works well.
You can use custom field for sorting and update pagination component.
Controller code
$order['Document.DATE'] = 'asc';
$this->paginate = array(
"conditions"=> $conditions ,
"order" => $order ,
"limit" => 10,
**"sortcustom" => array('field' =>'Document.DATE' , 'direction' =>'desc'),**
);
Changes in pagination component.
public function validateSort($object, $options, $whitelist = array()) {
if (isset($options['sort'])) {
$direction = null;
if (isset($options['direction'])) {
$direction = strtolower($options['direction']);
}
if ($direction != 'asc' && $direction != 'desc') {
$direction = 'asc';
}
$options['order'] = array($options['sort'] => $direction);
}
if (!empty($whitelist) && isset($options['order']) && is_array($options['order'])) {
$field = key($options['order']);
if (!in_array($field, $whitelist)) {
$options['order'] = null;
}
}
if (!empty($options['order']) && is_array($options['order'])) {
$order = array();
foreach ($options['order'] as $key => $value) {
$field = $key;
$alias = $object->alias;
if (strpos($key, '.') !== false) {
list($alias, $field) = explode('.', $key);
}
if ($object->hasField($field)) {
$order[$alias . '.' . $field] = $value;
} elseif ($object->hasField($key, true)) {
$order[$field] = $value;
} elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
$order[$alias . '.' . $field] = $value;
}
}
**if(count($options['sortcustom']) > 0 )
{
$order[$options['sortcustom']['field']] = $options['sortcustom']['direction'];
}**
$options['order'] = $order;
}
return $options;
}
Easy insert 'paramType' => 'querystring',
Show Code Example:
$this->paginate = array(
'conditions' => $conditions,
'order' => array(
'Post.name' => 'ASC',
'Post.created' => 'DESC',
),
'paramType' => 'querystring',
);
$this->set('posts', $this->paginate('Post'));

Resources