Am using yii\rest\ActiveController without pagelimit
This is how am doing it
<?php
namespace rest\modules\v1\controllers;
use yii\rest\ActiveController;
class CompanyController extends ActiveController
{
public $modelClass = 'frontend\models\TblDeliveredCompanies';
}
The above returns only 20 records how do i improve it to return all
You can override prepareDataProvider() inside the controller to return a custom data provider from index action:
public function prepareDataProvider()
{
$query = \frontend\models\TblDeliveredCompanies::find();
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
'pagination' => ['pageSize' => 0]
]);
return $dataProvider;
}
Check documentation at http://www.yiiframework.com/doc-2.0/guide-rest-controllers.html#extending-active-controller
The limit for 20 could be related to the default pagination values for dataProvider so in your controller/action or in the model where the $dataProvider is create you shoul redefine the function assign the pagination as false
$dataProvider->pagination = false;
or
$dataProvider = new ActiveDataProvider([
'query' => $your_query,
'pagination' => false,
]);
Check the code in the model class 'TblDeliveredCompanies' present at 'frontend\models\TblDeliveredCompanies' it must have the SQL query containing the limit 20, change it to 'SELECT * from yourtbl where %yourcond%'
Other solution
public function actions()
{
$actions = parent::actions();
$actions['index']['pagination'] = false;
return $actions;
}
Related
I have seen two ways of paginations I would like to know the differences between them. Or if there is a strict way to use either of them. Please don't mind the type of data used in the example.
First one
public function rules()
{
return [
'query_value' => 'string',
'status' => ['string', Rule::in(BaseModel::STATUS_CODES)],
'pagination' => 'integer',
];
}
public function paginationResult()
{
return !is_null($this->get('pagination')) && $this->get('pagination') > 0 ? $this->get('pagination') : 10;
}
in a controller
$this->itemsPerPage = $filterRequest->paginationResult();
Second one
$users = User::where('votes', '>', 100)->paginate(10);
FormRequest class
class UserRequest extends FormRequest
{
public function rules()
{
return [
'query_value' => 'string',
'status' => ['string', Rule::in(BaseModel::STATUS_CODES)],
'pagination' => 'integer',
];
}
public function paginationValue()
{
return $this->pagination ?: 10;
}
}
Then in controller
public function index(UserRequest $request)
{
$users = User::where('votes', '>', 100)->paginate($request->paginationValue());
//...rest of method code
}
The paginate() method will return an instance of Illuminate\Pagination\LengthAwarePaginator which means that you can display links like 1 2 ......9 10 - numbers show the page numbers for paginated results
Another way to paginate is using simplePaginate() which will return an instance of Illuminate\Pagination\Paginator, which means you can display links as Prev Next
Laravel 5.8
PHP 7.4
I want to load the relationships conditionally like
http://127.0.0.1:8000/api/posts
and
http://127.0.0.1:8000/api/posts/1 are my end points now, I want to load comments like
http://127.0.0.1:8000/api/posts/?include=comments and
http://127.0.0.1:8000/api/posts/1/?include=comments
If the query parameter is there, only then it should load comments with posts or it should load only posts/post
I am doing this by referring a blog post
now, RequestQueryFilter
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
class RequestQueryFilter
{
public function attach($resource, Request $request = null)
{
$request = $request ?? request();
return tap($resource, function($resource) use($request) {
$this->getRequestIncludes($request)->each(function($include) use($resource) {
$resource->load($include);
});
});
}
protected function getRequestIncludes(Request $request)
{
// return collect(data_get($request->input(), 'include', [])); //single relationship
return collect(array_map('trim', explode(',', data_get($request->input(), 'include', [])))); //multiple relationships
}
}
and in helper
<?php
if ( ! function_exists('filter') ) {
function filter($attach)
{
return app('filter')->attach($attach);
}
}
?>
in PostController
public funciton show(Request $request, Post $post) {
return new PostResource(filter($post));
}
but when I am trying to retrieve
http://127.0.0.1:8000/api/posts/1/?include=comments getting no comments, with no error in log
A work around will be PostResource
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'name' => $this->title,
'body' => $this->content,
];
$filter = $request->query->get('include', '');
if($filter){
$data[$filter] = $this->resource->$filter;
}
return $data;
}
I want to load the relationships conditionally like
Lazy Eager Loading using the load() call
The Lazy Eager Loading accomplishes the same end results as with() in Laravel, however, not automatically. For example:
?include=comments
// Get all posts.
$posts = Post::without('comments')->all();
if (request('include') == 'comments')) {
$posts->load('comments');
}
return PostResource::collection($posts);
Alternativelly, you could require the include query string to be an array:
?include[]=comments&include[]=tags
// Validate the names against a set of allowed names beforehand, so there's no error.
$posts = Post::without(request('includes'))->all();
foreach (request('includes') as $include) {
$posts->load($include);
}
return PostResource::collection($posts);
The call without() is only required in case you defined your model to automatically eager load the relationships you want to conditionally load.
With all data filtered in Controller, just make sure to display only loaded relations in your PostResource
public function toArray($request) {
$data = [...];
foreach ($this->relations as $name => $relation)
{
$data[$name] = $relation;
}
return $data;
}
I would create a custom resource for the posts with
php artisan make_resource
command.
E.g. PostResource.
The toArray function of the resource must return the data.
PostResource.php
public function toArray($request){
$data =['title' => $this->resource->title,
'body' => $this->resource->body,
'images' => new ImageCollection($this->whenLoaded('images')),
];
$filter = $request->query->get('filter', '');
if($filter){
$data['comments'] => new CommentCollection($this->resource->comments);
}
return $data;
}
Also, for collections, you need to create a ResourceCollection.
PostResourceCollection.php
class PostResourceCollection extends ResourceCollection
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}
In your controller:
PostsController.php
//show one post
public function show(Post $post, Request $request)
{
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResource($post));
}
//list of posts
public function index(Request $request)
{
$posts = Post::all();
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResourceCollection($posts));
}
Partial Solution
It will need a small change in resource class
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'comments' => new CommentCollection($this->whenLoaded('comments')),
'images' => new ImageCollection($this->whenLoaded('images')),
];
return $data;
}
and it will load comments and images if loaded and that depends on the include query parameter, if that is not included, it will not load the relationship.
However,
In post collection
return [
'data' => $this->collection->transform(function($post){
return [
'id' => $post->id,
'title' => $post->title,
'body' => $post->body,
'comments' => new CommentCollection($post->whenLoaded('comments')),
'images' => new ImageCollection($post->whenLoaded('images')),
];
}),
];
will results in
"Call to undefined method App\Models\Customer::whenLoaded()",, if anyone suggests a complete solution, it will be a great help, if I will able to do, it I will update here.
I created custom actions for rest api in yii2
my codes are:
namespace app\controllers;
use yii\rest\ActiveController;
use yii\web\Response;
use Yii;
class RsController extends ActiveController{
public $modelClass='app\models\Mymodel';
/*some another actions*/
public function actionOne($id){
return \app\models\Anothermodel::findAll(['my_id'=>$id]);
}
public function actionTwo($id){
return \app\models\Anothermodel::findAll(['my_name'=>'xxxx']);
}
}
I know we can override fields function in model to get special fields but
now I wanted to get different fields for actionOne and actionTwo (of a model)
How can I override fields function in Anothermodel for this purpose?
I found my answer from here
I create a component like this
<?php
namespace app\components;
class Serializer extends \yii\rest\Serializer {
public $defaultFields;
public $defaultExpand;
public function init() {
parent::init();
$this->defaultFields = !is_null($this->defaultFields) ? implode(",", $this->defaultFields) : $this->defaultFields;
$this->defaultExpand = !is_null($this->defaultExpand) ? implode(",", $this->defaultExpand) : $this->defaultExpand;
}
protected function getRequestedFields() {
$fields = is_null($this->request->get($this->fieldsParam)) ? $this->defaultFields : $this->request->get($this->fieldsParam);
$expand = is_null($this->request->get($this->expandParam)) ? $this->defaultExpand : $this->request->get($this->expandParam);
return [
preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY),
preg_split('/\s*,\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY),
];
}
}
and then in my controllers action set my fields
like this.
public function actionOne($id){
$this->serializer['defaultFields'] = ["field1",
"field2"];
return new \yii\data\ActiveDataProvider([
'query' => \app\models\Anothermodel::find()->where(['my_id'=>$id]),
]);
}
public function actionTwo($id){
$this->serializer['defaultFields'] = ["field1",
"field2","field3"];
return new \yii\data\ActiveDataProvider([
'query' => \app\models\Anothermodel::find()->where(['my_id'=>$id]),
]);
}
I suggest to use events
public function actionPublic()
{
\yii\base\Event::on(Thing::class, Thing::EVENT_AFTER_FIND, function ($event) {
$event->sender->scenario = Thing::SCENARIO_SEARCH_PUBLIC;
});
return new ActiveDataProvider([
'query' => Thing::find(),
]);
}
public function actionPrivate()
{
\yii\base\Event::on(Thing::class, Thing::EVENT_AFTER_FIND, function ($event) {
$event->sender->scenario = Thing::SCENARIO_SEARCH_PRIVATE;
});
return new ActiveDataProvider([
'query' => Thing::find(),
]);
}
and inside of ActiveRecord (Thing in my case) check the scenario in fields() method
public function fields()
{
$fields = parent::fields();
if ($this->scenario === self::SCENARIO_SEARCH_PUBLIC) {
unset($fields['field1'], $fields['field2'], $fields['field3'], $fields['field4']);
}
return $fields;
}
check my answer in gihub
I added
$query->joinWith('projectParticipants');
in search model but got this error:
relation is correct and returns data if I var_dump in view.
Tried as well
->leftJoin('project_participants', 'project_participants.user_id = user_cards.id')
but the same error. :(
With different relations the same problem and in Yii Debugger all queries are correct.
Where I could go wrong?
What is this empty property?
In model relation looks:
public function getProjectParticipants()
{
return $this->hasMany(ProjectParticipants::className(), ['user_id' => 'id']);
}
UPDATED
public function search($params)
{
$query = UserCards::find();
$query->joinWith('projectParticipants');
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => array('pageSize' => 45),
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'project_participants.proj_id' => $this->projects,
]);
$query->andFilterWhere(['ilike', 'name', $this->fullName ])->orFilterWhere(['like', 'surname', $this->fullName ]);
return $dataProvider;
}
The issue was with primary key - user_cards model was generated from db view where is no pk.
So the solution was to add
public static function primaryKey(){
return array('id');
}
in model.
Please can someone explain how the search method in a Yii2 SearchModel works? I generated it using Gii. Here it is:
public function search($params){
$query = MyModel::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'att1');
$this->addCondition($query, 'att1', true);
$this->addCondition($query, 'att2');
$this->addCondition($query, 'att2', true);
return $dataProvider;
}
This is how I call it:
$search = new MyModelSearch();
$myModels = $search->search(['att3' => '3']);
Regardless of what attributes I use in calling search, I always get back the same result - i.e. all the entries in the table. I'm missing something here that I just do not understand.
Any help would be really appreciated. Thanks.
The search() function generated by Gii use ActiveRecord::load() to set search parameters :
load() gets the 'FormName' from the model's formName() method (which you may override), unless the $formName parameter is given. If the form name is empty, load() populates the model with the whole of $data, instead of $data['FormName'].
So you should try :
$myModels = $search->search(['MyModelSearch'=>['att3'=>3]]);
Or
$myModels = $search->search([$search->formName()=>['att3'=>3]]);
And of course add a condition on att3 attribute in search() function :
$this->addCondition($query, 'att3');
But if you really want to use $myModels = $search->search(['att3' => '3']); then you should simply replace $this->load($params) with $this->load($params, '').
If you want some additional param to pass to search() method, you can change search method like this in SomeSearch.php:
public function search($params, $additional=0)
{
//...
if($additional==1) {
$query->andWhere(['status'=>['some', 'other']);
}
}
and inside controller:
public function actionIndex()
{
$searchModel = new AdminSearch();
$additional=1;
$dataProvider = $searchModel->search(Yii::$app->request->queryParams, $additional);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
$searchModel = new CursadoSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->query->andWhere([ 'cursado.curso_id' => $curso_id]);