Yii2 how to create a dataProvider dynamically - yii2

I have two models like below
$analytics=new CountryAnalytics();
$analytics->country="United Arab Emirates";
$analytics->totalAds=$uaeAds;
$analytics->totalUsers=$uaeUsers;
$analyticsKsa=new CountryAnalytics();
$analyticsKsa->country="United Arab Emirates";
$analyticsKsa->totalAds=$uaeAds;
$analyticsKsa->totalUsers=$uaeUsers;
$analtics and $analyticsKsa.These are building dynamically.So my concern is to add these models to an empty dataprovider like below
$dataProvider = new ActiveDataProvider();
$dataProvider->setData($analytics);
$dataProvider->setData($analyticsKsa);
But it is not the right way.How can i achieve this.Thanks in advance

For cases like this you want to use \yii\data\ArrayDataProvider instead of \yii\data\ActiveDataProvider
You can pass your models in array to the allModels property when creating its instance.
$dataProvider = new \yii\data\ArrayDataProvider([
'allModels' => [
$analytics,
$analyticsKsa,
]
]);

Related

How to implement Filtering on YII restful GET api?

I am working on Restful APIs of Yii.
My controller name is ProductsController and the Model is Product.
When I call API like this GET /products, I got the listing of all the products.
But, now I want to filter the records inside the listing API.
For Example, I only want those records Which are having a product name as chairs.
How to implement this?
How to apply proper filtering on my Rest API. I am new to this. So, I have no idea how to implement this. I also followed their documentation but unable to understand.
May someone please suggest me a good example or a way to achieve this?
First of all you need to have validation rules in your model as usual.
Then it's the controllers job and depending on the chosen implementation I can give you some hints:
If your ProductsController extends yii\rest\ActiveController
Basically the easiest way because almost everything is already prepared for you. You just need to provide the $modelClass there and tweak actions() method a bit.
public function actions()
{
$actions = parent::actions();
$actions['index']['dataFilter'] = [
'class' => \yii\data\ActiveDataFilter::class,
'searchModel' => $this->modelClass,
];
return $actions;
}
Here we are modifying the configuration for IndexAction which is by default responsible for GET /products request handling. The configuration is defined here and we want to just add dataFilter key configured to use ActiveDataFilter which processes filter query on the searched model which is our Product. The other actions are remaining the same.
Now you can use DataProvider filters like this (assuming that property storing the product's name is name):
GET /products?filter[name]=chairs will return list of all Products where name is chairs,
GET /products?filter[name][like]=chairs will return list of all Products where name contains word chairs.
If your ProductsController doesn't extend yii\rest\ActiveController but you are still using DataProvider to get collection
Hopefully your ProductsController extends yii\rest\Controller because it will already benefit from serializer and other utilities but it's not required.
The solution is the same as above but now you have to add it by yourself so make sure your controller's action contains something like this:
$requestParams = \Yii::$app->getRequest()->getBodyParams(); // [1]
if (empty($requestParams)) {
$requestParams = \Yii::$app->getRequest()->getQueryParams(); // [2]
}
$dataFilter = new \yii\data\ActiveDataFilter([
'searchModel' => Product::class // [3]
]);
if ($dataFilter->load($requestParams)) {
$filter = $dataFilter->build(); // [4]
if ($filter === false) { // [5]
return $dataFilter;
}
}
$query = Product::find();
if (!empty($filter)) {
$query->andWhere($filter); // [6]
}
return new \yii\data\ActiveDataProvider([
'query' => $query,
'pagination' => [
'params' => $requestParams,
],
'sort' => [
'params' => $requestParams,
],
]); // [7]
What is going on here (numbers matching the code comments):
We are gathering request parameters from the body,
If these are empty we take them from the URL,
We are preparing ActiveDataFilter as mentioned above with searched model being the Product,
ActiveDataFilter object is built using the gathered parameters,
If the build process returns false it means there is an error (usually unsuccessful validation) so we return the object to user to see list of errors,
If the filter is not empty we are applying it to the database query for Product,
Finally we are configuring ActiveDataProvider object to return the filtered (and paginated and sorted if applicable) collection.
Now you can use DataProvider filters just as mentioned above.
If your ProductsController doesn't use DataProvider to get collection
You need to create your custom solution.

Yii2 - Checkboxlist Value Store In Database

in my db structure
service_request type enum('towel','tissue','napkin')
then have a model
* #property string $service_request
then in my view
<?= $form->field($model, 'service_request')->checkBoxList([ 'towel' => 'Towel', 'tissue' => 'Tissue', 'napkin' => 'Napkin']) ?>
then when i choose towel, tissue and napkin then submit the form, it's have an error said
Service Request must be String
please help me
Thank You
Like Joji Thomas said, checkBoxList prodices an array.
You need to change your database structure so that it supports 1-to-many relations (each $model can have multiple service_requests) if you want to save this. Unfortunately Yii is not very good at this sort of thing out of the box so you have to do a bunch of things yourself.
First you need to create a ServiceRequest ActiveRecord.
Then your $model needs to have a relation like:
public function getServiceRequests() {
return $this->hasMany(ServiceRequest::className(), ['model_id' => 'id'];
}
Then in your controller (model create action) you will need to do something like this:
foreach (Yii::$app->request->post('ServiceRequest',[]) as $data) {
$item = new ServiceRequest($data);
$model->link('serviceRequests', $item);
}
If you wanna update the checkboxes too then you need to do something similar in your model update action as well.
Please change checkBoxList to radioList, because when selecting multiple values service_request becomes an array. Enum type can handle only string values.
First change your filed datatype from enum to varchar. enum only takes a single string value.
Secondly you need to implode service_request array to string for save to db.
Use bellow code before the model save function :
$model->service_request = implode("," , $model->service_request);
$model->save();

Symfony2 - entities with relationships as json response

I am trying to create efficient JSON Response controllers for AJAX. So far, instead of passing whole entity to JsonResponse I am creating arrays with necessary data inside where I can easily manage output data leaving less work for JavaScript. My action looks something like this:
public function getOffersAction(Request $request)
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(array('message' => 'You can access this only using Ajax!'), 400);
}
/** #var OfferRepository $offerRepository */
$offerRepository = $this->getDoctrine()->getRepository('IndexBundle:Offer');
$offers = $offerRepository->findBy(array('state' => 'available'));
$offersArray = array();
/** #var Offer $offer */
foreach ($offers as $offer) {
$areasArray = array();
foreach ($offer->getAreas() as $area) {
$areasArray[] = array(
'name' => $area->getName()
);
}
$offersArray[] = array(
'id' => $offer->getId(),
'code' => $offer->getCode(),
'title' => $offer->getTitle(),
'city' => $offer->getCity(),
'country' => $offer->getCountry()->getName(),
'latitude' => $offer->getLatitude(),
'longitude' => $offer->getLongitude(),
'areas' => $areasArray
);
}
return new JsonResponse($offersArray, 200);
}
It is all good, ajax is working fast.
At this point I started googling searching if this is a right approach to it. I found out about JMSSerializerBundle which serializes entities. I tried using it, but I am facing problems serializing relationships and how to access related entities data using JS. It is so complicated leaving so many proccessing to do for JS that I start doubting that it is a good approach.
What do you think? What is your experience with it? Which approach is better and why?
I prefer the symfony normalizer/serializer approach.
http://symfony.com/doc/current/components/serializer.html
As described, you can overide serializer to serialize your object in the same custom way for your whole application

How to use the search() function in yii2 to search for more than 1 condition?

In my controller in yii2 I have the following:
$searchModel = new HealthSearch();
$dataProvider = $searchModel->search(['HealthSearch'=>['zip'=>$zipcode]]);
which works but I would like it to also search for zipcodes and speciality
I tried:
$dataProvider = $searchModel->search(['HealthSearch'=>['zip'=>$zipcode,'pri_spec'=>$sspec']]);
but that does not work? What is the correct way of searching??
After removing the last single quoted '. Your code should work fine. An easier to read version may look like :
$searchByAttr['HealthSearch'] = [
'zip' => $zipcode,
'pri_spec' => $sspec
];
$dataProvider = $searchModel->search($searchByAttr);
Also you need to check the HealthSearch class which is the first responsible of making that search. Gii generates a primary boilerplate from your model that need to be adapted to your app in further steps. By default the HealthSearch::search() method should filter by all model's safe attributes and as any ActiveRecord class it has also a rules() method that returns those safe attributes. So if zip and pri_spec are not included in that array they will be simply ignored.

Mass update in Laravel Eloquent or DB

Is there anyone who knows how to do this without the technique of doing it in a one query string. I mean the popular ways I see on the net is by looping in data(the updates) and generating a single update statement and then fire a query. Is it possible for an Eloquent Approach or DB without looping?
This is posible with Eloquent, it might be necessary to enable mass-assignment, but you will get an error if so.
$post_data = Input::all();
$model = Model::find($id);
$model ->fill($post_data);
$model ->save();
or
$post_data = Input::all();
Model::find($id)->update($post_data);
Yes, you can do that but in that case, you have to make the array of data that is a loop is needed to store the data in the array with respective field_name => value of the table.
The following is the example:
$Array = array(); //This is needed to hold data while looping over $YourData
$YourData - is the array of data you want to store in the respective table.
foreach ($YourData as $YourDatakey => $YourDatavalue ){
$Array = [
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
...... and so on
];
}
$InsertQuery= YourModelName::create($Array);
PS:
YourModelName model file should have the columns in protected
$fillable = ['column1','column2'....];
You should use App\Models\ModelName; at the top of the file.