I have two ActiveRecord models: Category & Product with many-to-many relation (via ProductCategory model) & I'm trying to build REST API that will search & return products with name LIKE '%term%'
grouped by categories.
My ApiController:
<?php
namespace api\modules\v1\controllers;
use api\modules\v1\models\Category;
use yii\data\ActiveDataProvider;
use yii\rest\Controller;
use Yii;
use yii\web\BadRequestHttpException;
class ApiController extends Controller
{
const ITEMS_PER_PAGE = 50;
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
public function actionSearchProductsByTerm()
{
if (Yii::$app->request->get('term')) {
return new ActiveDataProvider([
'query' => Category::find()
->with([
'products' => function ($query) {
/** @var $query yii\db\ActiveQuery */
$query->andWhere(['like', 'name', Yii::$app->request->get('term')]);
}
]),
'pagination' => [
'pageSize' => self::ITEMS_PER_PAGE,
],
]);
} else {
throw new BadRequestHttpException('Missing term parameter in request');
}
}
}
But when I request api.my-site.loc/v1/searchProductsByTerm?term=iphone&expand=products I see following result:
{
"success": true,
"data": {
"items": [
{
"id": 2,
"name": "Apple technics",
"url": null,
"products": []
},
{
"id": 3,
"name": "Apple smartphones",
"url": null,
"products": []
},
{
"id": 4,
"name": "Apple tablets",
"url": null,
"products": []
},
{
"id": 5,
"name": "Apple notebooks",
"url": null,
"products": []
}
],
"_links": {
"self": {
"href": "http://api.my-site.loc/v1/searchProductsByTerm?term=iphone&expand=products&page=1&per-page=50"
},
"first": {
"href": "http://api.my-site.loc/v1/searchProductsByTerm?term=iphone&expand=products&page=1&per-page=50"
},
"last": {
"href": "http://api.my-site.loc/v1/searchProductsByTerm?term=iphone&expand=products&page=1&per-page=50"
}
},
"_meta": {
"totalCount": 4,
"pageCount": 1,
"currentPage": 1,
"perPage": 50
}
}
}
Question: why is a list of all categories on the site displayed, and not just those that contain products with name LIKE '%iphone%'
? At the same time, the array of products is empty everywhere for some reason. What did I miss? And another question: is it possible to somehow return an array of products by default, so as not to specify expand=products
every time (because for this API method this is redundant, since the search will always be performed by product attributes)?
My Answer:
In your current implementation, you are querying the Category model and trying to filter products within each category based on the search term. However, the issue lies in the way you are filtering the products within each category.
Instead of filtering the products within each category, you should directly query the Product model and filter the products based on the search term. Here's how you can modify your code:
php
public function actionSearchProductsByTerm()
{
if (Yii::$app->request->get('term')) {
return new ActiveDataProvider([
'query' => Product::find()
->where(['like', 'name', Yii::$app->request->get('term')]),
'pagination' => [
'pageSize' => self::ITEMS_PER_PAGE,
],
]);
} else {
throw new BadRequestHttpException('Missing term parameter in request');
}
}
With this modification, you are directly querying the Product model and filtering the products based on the search term. This should return the list of products that match the search term.
Regarding your second question about returning an array of products by default without specifying expand=products, you can achieve this by modifying the serializer configuration in your controller. You can set the expand parameter to include the 'products' relation by default. Here's how you can do it:
php
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
'expand' => ['products'],
];
By setting the 'expand' parameter in the serializer configuration, the 'products' relation will be included by default in the response without the need to specify expand=products in the request URL.
With these modifications, your API should now return the list of products that match the search term grouped by categories.
Rate this post
4 of 5 based on 6813 votesComments