I have products table and productlines table. Product has relationship with the productlines. I want to have a single search box and search the fields from product table and fields from productlines table.
My ProductSearch Model
public function search($params)
{
$query = Product::find()->where(['product_id' => $this->getProductID()]);
// add conditions that should always apply here
$query->joinWith('productlines');
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'product_id' => $this->product_id,
'product_region' => $this->product_region,
'product_created' => $this->product_created,
'product_lastchecked' => $this->product_lastchecked,
'sdsref_id' => $this->sdsref_id,
]);
// var_dump($this->getProductID()); exit();
$query->andFilterWhere(['like', 'product_name', $this->product_name])
->andFilterWhere(['like','product_id', $this->product_id])
->orFilterWhere(['like', 'product_catalog', $this->code])
->andFilterWhere(['=', 'product_aka', $this->product_aka])
->orFilterWhere(['like', 'internal_code' , $this->code]);
return $dataProvider;
}
When I do this I get the error:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'product_id' in where clause is ambiguous
The SQL being executed was: SELECT COUNT(*) FROM `sim_product` LEFT JOIN `sim_productlines` ON `sim_product`.`product_id` = `sim_productlines`.`product_id` WHERE ((`product_id` IN ('2', '3')) OR (`product_catalog` LIKE '%A%')) OR (`internal_code` LIKE '%A%')
Can anyone assist me where am I going wrong and what can be the possible solution.
Thanks
Yii2 allows you to prefix column names within ActiveQuery such as:
$query->andFilterWhere(['like', 'product.product_name', $this->product_name])
->andFilterWhere(['like','productline.product_id', $this->product_id])
->orFilterWhere(['like', 'productline.product_catalog', $this->code])
->andFilterWhere(['=', 'product.product_aka', $this->product_aka])
->orFilterWhere(['like', 'product.internal_code' , $this->code]);
Source: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#relational-data
See the section "Joining with relations"
Related
How can i use an array of value to compare inside the where clause like in the below code, 'frompersons' is an array of names coming in response from the first call and I want to get out their info from 'chatterusers' database. But how can i use this array inside the next where clause ?
return knex('frndrqst').where({ toperson: toperson })
.select('fromperson')
.then(frompersons => {
db.select('*').from('chatterusers')
.where( 'name', '=', frompersons )
.then(data => {
res.json(data);
})
.catch(err => res.json("Unable to load frndrqsts !!!"))
})
.catch(err => res.json("Unable to load frndrqsts !!!"))
//get the list of name from 'formperson' table
var subquery = knex.select('name').from('fromperson');
//get in all information from 'chatterusers table' that name is equal with name
return knex.select('*').from('chatterusers')
.whereIn('name', subquery)
output :
select * from chatterusers where name in (select name from fromperson)
There is a serch form on the mainpage of a realestate agency. The data about objects is stored in the table "realty" that uses relations. For example, there are related tables category (residential, commercial, land plots), deal (buy, sell, rent), object_type (apartment, house, office).
Then different categories have different properties and and there are three bootstrap tabs in the search form: residential, commercial, land plots. Under each tab there are selects and input fields that are specific for the choosen tab.
In the most cases, the examples of using Search model are given within a gridview.
Is it possible to adapt Search model logic so that it could return the array of results from the table 'realty' based on the values indicated in the search form on the mainpage ?
Yes, of course you can. you have several options:
Solution 1 - worst solution but the answer to your question
modify the search function of the model (or create a new function). The search functions usually looks like this
public function search($params)
{
$query = Bookmark::find()->where('status <> "deleted"');
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => Yii::$app->session->get(get_parent_class($this) . 'Pagination'),
],
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere([
'status' => $this->status,
'id' => $this->id,
'reminder' => $this->reminder,
'order' => $this->order,
'update_time' => $this->update_time,
'update_by' => $this->update_by,
'create_time' => $this->create_time,
'create_by' => Yii::$app->user->id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'url', $this->url]);
return $dataProvider;
}
You can change it to something like
public function search($params)
{
$query = Bookmark::find()->where('status <> "deleted"');
if (!($this->load($params) && $this->validate())) {
THROW AN ERROR SOMEHOW HERE
}
$query->andFilterWhere([
'status' => $this->status,
'id' => $this->id,
'reminder' => $this->reminder,
'order' => $this->order,
'update_time' => $this->update_time,
'update_by' => $this->update_by,
'create_time' => $this->create_time,
'create_by' => Yii::$app->user->id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'url', $this->url]);
return $query->all();
}
however this will return to you all the records because ActiveDataProvider takes care of the pagination based on the query given.
Solution 2, a better solution
read the first example here http://www.yiiframework.com/doc-2.0/yii-data-activedataprovider.html . You can call ->getModels() on an ActiveDataProvider to get the actual records. No changes needed to the search function. Do whatever you want with the array.
Solution 3 and what I use all the time
Use ActiveDataProvider with a ListView. The list view allows you to create the list of records however you want, it does not have to be a table. I personally do this in many places and it works quite well. I sometimes transform arrays to an ArrayDataProvider just to use it. More about data providers here: http://www.yiiframework.com/doc-2.0/guide-output-data-providers.html
I'm using Bookshelf.js/Knex.js, fetching a model (call it user) with a related child model (call it company).Can I order by a field on the child model - company.name?
Also, if that's possible, can I multi sort, say company.name descending then lastName ascending
Here's my current code, which only works on root model fields. qb.orderBy('company.name', 'desc') doesn't work.
users.query(function(qb) {
qb.orderBy('lastName', 'asc');
})
.fetch({withRelated: ['company']})
.then(success, error);
Try the following:
users
.fetch({withRelated: [
{
'company': function(qb) {
qb.orderBy("name");
}
}
]})
.then(success, error);
I got the idea from https://github.com/tgriesser/bookshelf/issues/361
You can do it like this without the need of a function:
users.query(function(qb) {
qb.query('orderBy', 'lastName', 'asc');
})
.fetch({withRelated: ['company']})
.then(success, error);
Found here: Sort Bookshelf.js results with .orderBy()
I think I solved it by doing this:
let postHits =
await posts
.query(qb => qb
.innerJoin('post_actor_rel', function () {
this.on('post.id', '=', 'post_actor_rel.post_id');
})
.innerJoin('actor', function () {
this.on('post_actor_rel.actor_id', '=', 'actor.id');
})
.orderByRaw('actor.name ASC')
.groupBy('id')
)
.fetchPage({
withRelated: ['roles', 'themes', 'activity_types', 'subjects', 'educational_stages', 'images', 'documents', 'actors'],
limit,
offset
},
);
I modify the query by inner joining with the desired tables and after sorting (using orderByRaw since I will need to add some more sorting that I think is not possible with orderBy) I group by the post's id to get rid of the duplicate rows. The only problem is that it's not defined which actor name (of several possible) is used for the sorting.
I'm using the CakePHP Search Plugin, and there's something I can't get to work. I would like to have a search field (let's call it age) where a user can input a value (like 24, for example) and get as a result all the elements with a value in the age field of the table who have age 24 or higher. How can I do that?
So far I can perform normal searches, like by name or by an exact value (if you want age 24 exactly then it works fine).
Here's the code related with the Search Plugin that I have so far:
- model:
class Person extends AppModel{
var $name = 'Person';
public $actsAs = array('Searchable');
public $filterArgs = array(
array('name' => 'name', 'type' => 'like'),
array('name' => 'age', 'type' => 'value'),
);
}
- controller:
class PersonsController extends AppController {
var $name = 'Persons';
var $components = array('Search.Prg');
public $presetVars = array(
array('field' => 'name', 'type' => 'value'),
array('field' => 'age', 'type' => 'value'),
);
function index() {
$this->set('persons', $this->Person->find('all'));
}
/* ---------------- SEARCH PLUGIN --------------*/
public function find() {
$this->Prg->commonProcess();
$this->paginate['conditions'] = this->Game->parseCriteria($this->passedArgs);
$this->set('persons', $this->paginate());
}
}
- find.ctp:
//apart from the code below, I have all the code to show the results
echo $form->create('Game', array(
'url' => array_merge(array('action' => 'find'), $this->params['pass'])
));
echo $form->input('name', array('div' => false));
echo $form->input('age', array('div' => false));
echo $form->submit(__('Search', true), array('div' => false));
echo $form->end();
To sum up: with the code above, I can perfomr a search of an exact value for the age field. How do I change that to perform a search of 'age >= value entered'?
THank you so much in advance!
So this is how I solved it (or at least it seems to work with all the test I've done so far):
Changed the 'age' line by the following line in the filterArgs array in the model:
array('name' => 'age', 'type' => 'query', 'method' => 'findByAge', 'field' => 'Person.age'),
Added the following method in the same model:
public function findByAge($data = array()) {
$query = array(
'Person.age >=' => $data['age'],
);
return $query;
}
That's it! With the 'query' type, any complex query can be added to the main query using a method. Thanks to satyrwilder for pointing in the right direction!
From your presetVars -
array('field' => 'age', 'type' => 'value'),
-- filters down to your find() method from your commonProcess() method, which I presume constructs a generalized find like conditions=>array($key=>$val)?
Cake sees array key=>val pair assignments as an indication to generate a "WHERE $key=$val" condition.
To do a fuzzier pull, wherever you construct that generic find() - add a method or something just to roll those two into
'conditions'=>array("name LIKE '%{$name}%'", "{$age}>={$val}")
or whatever you've got going on. String the arguments together for the desired effect on your query and I'm sure you can extrapolate further ways to abstract that query, if needed.
If you need a more complex solution, Cake provides elegant built-in, extremely powerful subquery generation. It seems like you're working awfully hard to achieve a lot of built-in functionality, but then again I don't know how complicated your situation is.
http://book.cakephp.org/view/1030/Complex-Find-Conditions
HTH
I know this has been answered but there is another method that may be more suitable to this situation.
From the documentation
'expression' type: useful if you want to add condition that will generate by some method, and condition field contain several parameter like in previos sample used for 'range'. Field here contains 'Article.views BETWEEN ? AND ?' and Article::makeRangeCondition returns array of two values.
Full Article Here
I had to accomplish the same thing you did but with 5 different fields. I did not want to have to write a query for each one. Here is how I accomplished the same thing with the expression type. This is in the filterArgs array.
array(
'name'=>'bathrooms',
'type'=>'expression',
'method'=>'returnNumber',
'field'=>'House.bathrooms >= ?'
)
Then I created this function in the model that simply returns the number the user submitted. When called, returnNumber will be passed two arrays.
Looks something like this.
Array
(
[0] => Array
(
[bathrooms] => 6
[bedrooms] => 5
[acres] => 12
[city] => Greenville
[year] => 2009
)
[1] => Array
(
[name] => bathrooms
[type] => expression
[method] => returnNumber
[field] => House.bathrooms >= ?
)
)
Now for the actual function. You can use the debug statement to see what you are actually passing the function.
function returnNumber($arg, $name){
// debug(func_get_args());
// exit;
return $arg[$name['name']];
}
I have a CGridView widget for Lesson model
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'lesson-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
... and Lesson has relation to the User model:
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
... and the CGridView has a column with user's lastname from the BELONGS_TO model described above
'columns'=>array(
...
array(
'name' => 'user',
'header'=>'Teacher',
'value' => '$data->user->lastname',
),
So, I can't symply search with CGridView in this column, but I need it.
How to search in '$data->user->secondname' with CGridView?
I think that I should extend search method in Lesson model, but how?
Now it looks like this:
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id);
$criteria->compare('student',$this->student,true);
$criteria->compare('comment',$this->comment,true);
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
));
}
This should work, add it to your search criteria in the search() method:
$criteria->with[]='user';
$criteria->addSearchCondition("user.secondname",$this->user_id);
This is what I do though:
if(!intval($this->user_id) && is_string($this->user_id) && strlen($this->user_id) > 0) {
$criteria->with[]='user';
$criteria->addSearchCondition("user.secondname",$this->user_id);
} else
$criteria->compare('t.user_id',$this->user_id);
And here is the CGridView definition:
'columns'=>array(
...
array(
'name' => 'user_id',
'header'=>'User',
'sortable'=>false, // since it would still be sorting based on ID
// 'value' => '$data->user->lastname', // basic version
'value'=>'CHtml::link((isset($data->user))?$data->user->username:$data->user_id,array("user/view","id"=>$data->user_id))', // link version
),
It's a fun little trick: if the search term is a string and NOT an intval(), it searches for the user by their secondname, via the "user" relation. But if you enter a user_id, it will find the user by their user_id - the default search() functionality.
NOTE: This will enable filtering, but it will still sort based on ID. You will need to implement something additional to get the sorting to work.
There are surely other ways to do this, but this is how I do it. I suspect there is a "right" to do this using the relation, but my technique works solid.