searching and filtering in yii - yii

How to filter the search results as a combination of checkboxes and dropdown in Yii? I have some categories as checkboxes and budget as dropdown that is listing from database. WIth multiple checkboxes checked and selecting a value from dropdown, how can I filter out the search results.. Is there any better way ?? (My requirement is like in this link. http://www.ebay.in/sch/i.html?_from=R40&_sacat=0&_nkw=pendrives&rt=nc&LH_FS=1 )

I recommend to use a search model. This could look like this:
class SearchProducts extends CFormModel
{
public $minPrice;
public $maxPrice;
public $categories;
// Add a public property for each search form element here
public function rules()
{
return array(
// You should validate your search parameters here
array('minPrice,maxPrice,categories', 'safe'),
);
}
public function search()
{
$criteria = new CDbCriteria;
if(!empty($this->minPrice))
$criteria->addCondition('price > '.(int)$this->minPrice);
if(!empty($this->maxPrice))
$criteria->addCondition('price < '.(int)$this->maxPrice);
if(!empty($this->categories))
$criteria->addInCondition('category_id', $this->categories);
// Add more conditions for each property here
return new CActiveDataProvider('Product', array(
'criteria' => $criteria,
// more options here, e.g. sorting, pagination, ...
));
}
}
In your controller you create a new instance of the search form and assign search variables as usual:
public function actionProducts()
{
$searchModel = new ProductSearch();
if(isset($_POST['ProductSearch']))
$searchModel->attributes = $_POST['ProductSearch'];
$this->render('products', array(
'searchModel' => $searchModel,
));
}
Finally in your view you can now render
A regular Yii form for $searchModel properties which will be your search filter form
A CListView or a CGridView where you set the provider to $searchModel->search() which will be your search results.
For the checkboxes you'd use a checkBoxList:
<?php $this->formCheckBoxList($model, 'categories[]', Category::opts()) ?>
Note the [] which indicates that this should be posted as array. For convenience i usually also implement a static opts() method in some models, which returns a list of id=>name that you can use for dropDownList or checkBoxList options.
This is only the basic pattern. You can easily extend it to create really powerful search forms and result list. Just always keep in mind that you should keep all search related data in another model.

try this extension: ddautofilter
https://bitbucket.org/jwerner/yii-ddautofilter/overview

Related

Where and how to set an SQL request for a form element?

I read a lot of things of things in the cookbook or on stackoverflow but I can't find something adapted and clearly explained about how to solve my problem.
I have an SQL request that allows me to find schools that teaches a specific subject. I need to set this SQL request so that when I load the page containing my form, the request is done and I see the result (a list of school) in a multi select form. My best guess is that I need to set it inside my controller, but then again, I'm not even sure of that since it's the first time that I need to do that
I don't know if I should show you any code, so ask if you need to see something!
Thank you in advance
edit Here is my formType
<?php
namespace MissionBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use SocieteBundle\Entity\Societe;
class PublicType extends AbstractType{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('public')
->add('ecolesDispo')
// My goal is to replace 'ecolesDispo' (that is currently a one-to-many)
// by my SQL request.
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MissionBundle\Entity\Mission'
));
}
}
Surely not in the controller. You need to use the "query_builder" attribute when you add the field to the form. Take a look at this example, from the Symfony cookbook : http://symfony.com/doc/current/reference/forms/types/entity.html#using-a-custom-query-for-the-entities
And you should translate your raw SQL query into DQL, so it's database-agnostic.
UPDATE about using a query builder with native SQL : http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/native-sql.html#the-nativequery-class
So something like this following piece of code should work (you'll have to edit some property names and fill the query though)
$builder->add('ecolesDispo', EntityType::class, array(
'class' => 'AppBundle:Ecole',
'query_builder' => function (EntityRepository $er) {
return $er->createNativeQuery('SELECT * FROM ecoles WHERE [...]');
},
'choice_label' => 'title',
));

Modelname and its behaviors do not have a method or closure named "getData". in yii

i dont know where am i going wrong i did play round with it but did not achieve anything.
i wanna display data using cListview but unable to do so
i have a function in model
Model
public function psearch1()
{
$name=$_GET['search'];
$criteria=new CDbCriteria;
$criteria->alias="t";
$criteria->select="t.id,t.name,t.model";
$criteria->condition='name LIKE "%'.$name.'%"';
return new CActiveDataProvider($this,array('criteria'=>$criteria,));
}
controller
public function actionPsearchindex()
{
$dataProvider=new Modelname('psearch');
$this->render('psearchindex',array(
'dataProvider'=>$dataProvider,
));
}
view
psearchindex
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_psearchindex1',
));
_psearchindex
echo CHtml::encode($data->name);
when i execute i get the following
error
Modelname and its behaviors do not have a method or closure named "getData".
this is my first attempt am doing so but unable to figure out whats wrong
a new model is not a data provider, call your custom search on it
public function actionPsearchindex()
{
//$dataProvider=new Modelname('psearch');// a new model is not a data provider
$model = new Modelname('psearch');
$this->render('psearchindex',array(
'dataProvider'=>$model->psearch1(), // this will give you a data provider that you can use
));
}

Two Different Search Functions

I have two search functions. One for public and one for admin search. They are exactly the same:
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
//$criteria->compare('ID',$this->ID);
$criteria->compare('t.IDkorisnik',Yii::app()->user->getId());
$criteria->compare('start_time',$this->start_time,true);
$criteria->compare('end_time',$this->end_time,true);
//$criteria->compare('information',$this->information,true);
$criteria->compare('country',$this->country,true);
$criteria->compare('city',$this->city,true);
$criteria->compare('start_price',$this->start_price);
$criteria->compare('min_bid',$this->min_bid);
$criteria->compare('valuta',$this->valuta,true);
$criteria->compare('title',$this->title,true);
$criteria->with=array('relationIDuzgajivac','relationIDgolub');
$criteria->compare('username',$this->uzgajivacSearch);
$criteria->compare('brojgoluba',$this->golub_search, true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination'=>array('pageSize'=>50),
));
}
except in search() I have this
$criteria->compare('t.IDkorisnik',Yii::app()->user->getId());
in publichsearch() I don't.
As you may see there is relation search. when I open admin page it works in publicsearch page it doesn't work, I don't know why
I never realized that when I'm creating new action
public function actionPublicSearch()
{
$model=new Auction('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['Auction']))
$model->attributes=$_GET['Auction'];
$this->render('publicsearch',array(
'model'=>$model,
));
}
this $model=new Auction('search'); within model always has to be 'search' not something like 'public_search'
and then within view, where CGridView is created
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'auction-grid',
'dataProvider'=>$model->public_search(),
dataProvider is a place where I put my search function, in this case public_search

Yii cgridview using dynamic active record model

I'm using the Yii-dynamic-active-record model class to get and put data to various database tables. Now I'd like to use cgridview to quickly build some views of this data, but I'm getting hung-up on writing a search function for this class.
I know that I could always do a foreach loop within the view and make my own html table, but if I could get the Yii way working that would be great for DRY.
*All the db tables have a ID column and this is the only column that really needs to be displayed... if you could get all the columns that would just be bonus points.
Thanks :)
Model
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('ID',$this->ID);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
View
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'entry-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'ID',
),
));
Controller
$tablename=$_GET['tname'];
$table=Entry::forTable($tablename);
$model=$table->findAll();
if(isset($_GET['Entry']))
$model->attributes=$_GET['Entry'];
$this->render('all',array(
'model'=>$model,
));
I am a little confused by what you have done here, however I think I have a solution. First off, in line 3 of your controller code, you call $model = $table->findAll(); this is likely not what you intended, as it will provide you with an ActiveRecord[]. What you want to do is to pass your $table into the gridview. So your controller code would look like this:
$tablename=$_GET['tname'];
$model=Entry::forTable($tablename);
if(isset($_GET['Entry']))
$model->attributes=$_GET['Entry'];
$this->render('all',array(
'model'=>$model,
));
you then need to pass in the table name to the CActiveDataProvider in your model:
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('ID',$this->ID);
return new CActiveDataProvider($this->tableName(), array(
'criteria'=>$criteria,
));
}
now for the "yucky" part, you need to edit the static model method of CActiveRecord to this:
if(isset(self::$_models[$className]))
return self::$_models[$className];
else
{
if(class_exists($className)){
$model=self::$_models[$className]=new $className(null);
$model->_md=new CActiveRecordMetaData($model);
$model->attachBehaviors($model->behaviors());
}
else{
$model = Entry::forTable($className);
}
return $model;
}
This tells it to load from the Entry table when a class of that name does not exist. This practice is discouraged, however its the only way I see of getting around your issue.

Yii form and model for key-value table

I have a table which has only two column key-value. I want to create a form which allow user insert 3 pair of key-value settings.
Do I need pass 3 different models to the view? Or is there any possible way to do this?
Check out this link:
http://www.yiiframework.com/doc/guide/1.1/en/form.table
This is considered best form in Yii for updating for creating multiple models.
In essence, for creation you can create a for loop generate as many inputs a you wish to have visible, and in your controller loop over the inputs to create new models.
View File:
for ( $settings as $i=>$setting ) //Settings would be an array of Models (new or otherwise)
{
echo CHtml::activeLabelEx($setting, "[$i]key");
echo CHtml::activeLabelEx($setting, "[$i]key");
echo CHtml::error($setting, "[$i]key");
echo CHtml::activeTextField($setting, "[$i]value");
echo CHtml::activeTextField($setting, "[$i]value");
echo CHtml::error($setting, "[$i]value");
}
Controller actionCreate:
$settings = array(new Setting, new Setting, new Setting);
if ( isset( $_POST['Settings'] ) )
foreach ( $settings as $i=>$setting )
if ( isset( $_POST['Setttings'][$i] ) )
{
$setting->attributes = $_POST['Settings'][$i];
$setting->save();
}
//Render View
To update existing models you can use the same method but instead of creating new models you can load models based on the keys in the $_POST['Settings'] array.
To answer your question about passing 3 models to the view, it can be done without passing them, but to validate data and have the correct error messages sent to the view you should pass the three models placed in the array to the view in the array.
Note: The example above should work as is, but does not provide any verification that the models are valid or that they saved correctly
I'm going to give you a heads up and let you know you could potentially make your life very complicated with this.
I'm currently using an EAV patterned table similar to this key-value and here's a list of things you may find difficult or impossible:
use CDbCriteria mergeWith() to filter related elements on "value"s in the event of a search() (or other)
Filtering CGridView or CListView
If this is just very straight forward key-value with no related entity aspect ( which I'm guessing it is since it looks like settings) then one way of doing it would be:
create a normal "Setting" CActiveRecord for your settings table (you will use this to save entries to your settings table)
create a Form model by extending CFormModel and use this as the $model in your form.
Add a save() method to your Form model that would individually insert key-value pairs using the "Setting" model. Preferably using a transaction incase a key-value pair doesn't pass Settings->validate() (if applicable)
optionally you may want to override the Form model's getAttributes() to return db data in the event of a user wanting to edit an entry.
I hope that was clear enough.
Let me give you some basic code setup. Please note that I have not tested this. It should give you a rough idea though.:
Setting Model:
class Setting extends CActiveRecord
{
public function tableName()
{
return 'settings';
}
}
SettingsForm Model:
class SettingsForm extends CFormModel
{
/**
* Load attributes from DB
*/
public function loadAttributes()
{
$settings = Setting::model()->findAll();
$this->setAttributes(CHtml::listData($settings,'key','value'));
}
/*
* Save to database
*/
public function save()
{
foreach($this->attributes as $key => $value)
{
$setting = Setting::model()->find(array('condition'=>'key = :key',
'params'=>array(':key'=>$key)));
if($setting==null)
{
$setting = new Setting;
$setting->key = $key;
}
$setting->value = $value;
if(!$setting->save(false))
return false;
}
return true;
}
}
Controller:
public function actionSettingsForm()
{
$model = new Setting;
$model->loadAttributes();
if(isset($_POST['SettingsForm']))
{
$model->attributes = $_POST['SettingsForm'];
if($model->validate() && $model->save())
{
//success code here, with redirect etc..
}
}
$this->render('form',array('model'=>$model));
}
form view :
$form=$this->beginWidget('CActiveForm', array(
'id'=>'SettingsForm'));
//all your form element here + submit
//(you could loop on model attributes but lets set it up static for now)
//ex:
echo $form->textField($model,'fieldName'); //fieldName = db key
$this->endWidget($form);
If you want further clarification on a point (code etc.) let me know.
PS: for posterity, if other people are wondering about this and EAV they can check the EAV behavior extention or choose a more appropriate DB system such as MongoDb (there are a few extentions out there) or HyperDex