Laravel pagination append data in one variable and other in another variable - php

I am facing a problem in laravel pagination. In laravel when I called paginate() method it returns
{
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"first_page_url": "http://laravel.app?page=1",
"last_page_url": "http://laravel.app?page=4",
"next_page_url": "http://laravel.app?page=2",
"prev_page_url": null,
"path": "http://laravel.app",
"from": 1,
"to": 15,
"data":[
{
// Result Object
},
{
// Result Object
}
]
}
This type of Object. What I want is that I want to set data in one vairable for example $a and except data all other value in $b.
But when I added appends('data') of my paginate variable it did not working correctly. I did not find a solution after googling it. Please help me to solve this.
Here is User model
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Authenticatable {
use Notifiable;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password', 'status', 'role_id',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
My Controller Code is
public function index() {
$users = User::where('status', 1)->paginate(10);
return response()->json(
[
'error' => 0,
'errorMsg' => '',
'data' => [
'users' => $users->appends('data')->toArray(),
],
]
);
}
I tried this code
return response()->json(
[
'error' => 0,
'errorMsg' => '',
'data' => [
'users' => $users->only($user['data'])->toArray(),
'users_pagination' => $users->except($user['data'])->toArray(),
],
]
);
In this users work correctly but users_pagination not working correctly. In both the users, users_pagination returns same value

Try this
$paginateData = User::where('status', 1)->paginate(10);
$arrPaginateData = $paginateData->toArray();
$users = $arrPaginateData['data'];
unset($arrPaginateData['data']); //remove data from paginate array
$pageInfo = $arrPaginateData;
Return in response
return response()->json(
[
'error' => 0,
'errorMsg' => '',
'data' => [
'users' => $users,
'users_pagination' => $pageInfo
],
]
);

Why not try to iterate the object? the below code will attached user specific data into each users.
public function index() {
$users = User::where('status', 1)->paginate(10);
foreach($users as $users){
$users->data = 'your data here';
}
return response()->json([
'error' => 0,
'errorMsg' => '',
'data' => [
'users' => $users,
],
]
);
}
If you want to use Laravel appends you have to follow as per the document.

Related

Whenever I use Laravel resource with paginate method, I get a response with "links" and "meta" keys. I want to change these keys to a new format

I used laravel resources for my api responses and added paginate method. Upon using paginate method I always get a result like this where laravel by default gives three keys namely "data", "links" and "meta". But I want to change the resource to my own need.
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28#example.com",
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort#example.com",
}
],
"links":{
"first": "http://example.com/pagination?page=1",
"last": "http://example.com/pagination?page=1",
"prev": null,
"next": null
},
"meta":{
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/pagination",
"per_page": 15,
"to": 10,
"total": 10
}
}
But I want a result like this
{
"data": [
{
"id": 1,
"name": "Eladio Schroeder Sr.",
"email": "therese28#example.com",
},
{
"id": 2,
"name": "Liliana Mayert",
"email": "evandervort#example.com",
}
],
"metadata": {
"pagination": {
"offset": 50,
"limit": 25,
"previousOffset": 25,
"nextOffset": 75,
"currentPage": 3,
"pageCount": 40,
"totalCount": 1000
}
}
}
How can I be able to achieve this. I am using Laravel 7.*
My controller code:
public function index(Request $request)
{
return DiscussionResource::collection($this->discussion->getDiscussionList($request));
}
My Model method looks like this:
public function getDiscussionList($request){
return $this->ofSearch($request)
->orderBy('created_at', config('settings.pagination.order_by'))
->paginate(config('settings.pagination.per_page'));
}
My resource looks like this:
class DiscussionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'question_id' => $this->question_id,
'user_id' => $this->user_id,
'user_image' => $this->user->userProfile->image,
'user_role' => $this->user->type,
'comment' => $this->comment,
'is_pinned' => $this->is_pinned,
'created_at' => $this->created_at->toDateString()
];
}
}
There are so many ways to do that in laravel and here you go 2 ways of it:
First Way: you can create a custom PaginatedResourceResponse and override the default paginationLinks. for example like below:
use Illuminate\Http\Resources\Json\PaginatedResourceResponse;
class CustomPaginatedResourceResponse extends PaginatedResourceResponse
{
protected function paginationLinks($paginated)
{
return [
'prev' => $paginated['prev_page_url'] ?? null,
'next' => $paginated['next_page_url'] ?? null,
];
}
protected function meta($paginated)
{
$metaData = parent::meta($paginated);
return [
'current_page' => $metaData['current_page'] ?? null,
'total_items' => $metaData['total'] ?? null,
'per_page' => $metaData['per_page'] ?? null,
'total_pages' => $metaData['total'] ?? null,
];
}
}
then override toResponse method(Actually, the toResponse method converts resource collection to responses)
public function toResponse($request)
{
return $this->resource instanceof AbstractPaginator
? (new CustomPaginatedResourceResponse($this))->toResponse($request)
: parent::toResponse($request);
}
You may consider overriding other methods if you want to customize your response furthermore.
Second Way: you can just override the toResponse method in the ResourceCollection and make it as you wish!
More Ways HERE!
If you want to customize the metadata you can take the help of the with() method that comes with laravel for collections.
// in DiscussionResource file
public function with($request)
{
return [
'meta' => [
'key' => 'value',
],
];
}
If you want to customize it from controller you may do something like this
return (DiscussionResource::collection($this->discussion->getDiscussionList($request)))
->additional(['meta' => [
'key' => 'value',
]]);
And in case you want it for a single resource you can modify it at the toArray() method
public function toArray($request)
{
return [
'data' => $this->collection,
'links' => [
'self' => 'link-value',
],
];
}
For more details you can check this out https://laravel.com/docs/7.x/eloquent-resources#adding-meta-data

Laravel 6 with function not showing related data

I've made a really simple function in Laravel 6 that uses with():
$forms = Form::with('itemsForms')->get();
return response()->json([
'code' => 200,
'data' => $forms
]);
The following are the relationships between Form and ItemsForm:
//Form
protected $fillable = [
'title',
'subtitle',
'text',
'name',
'email',
'phone_number',
'address',
'board',
'date',
'file',
'purchasable',
'payment_for',
'invoice_amount',
];
protected $visible = [
'title',
'subtitle',
'text',
'name',
'email',
'phone_number',
'address',
'board',
'date',
'file',
'purchasable',
'payment_for',
'invoice_amount',
];
public function itemsForms()
{
return $this->hasMany('App\ItemsForm');
}
//ItemsForm
protected $fillable = [
'item_id', 'form_id'
];
public function form()
{
return $this->belongsTo('App\Form', 'form_id');
}
The thing is it doesn't retrieve any data from ItemsForm.
Here's some of what I've tried:
I tried changing parameter in with to other similar names but in each case I got an error of "relationship not found" or something like that. When I use itemsForms I get no error.
I tried debugging it enabling the query log. Here's what I got:
array:2 [
0 => array:3 [
"query" => "select * from `forms`"
"bindings" => []
"time" => 5.77
]
1 => array:3 [
"query" => "select * from `items_forms` where `items_forms`.`form_id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)"
"bindings" => []
"time" => 1.03
]
]
I tried getting the ItemsForm data and it retrieves it with no issue (ItemsForm::all()).
Any idea of what could be causing this?
Edit: the schema for ItemsForm is the following:
Schema::create('items_forms', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('item_id');
$table->unsignedBigInteger('form_id');
$table->foreign('item_id')
->references('id')->on('items')
->onDelete('no action')
->onUpdate('no action');
$table->foreign('form_id')
->references('id')->on('forms')
->onDelete('no action')
->onUpdate('no action');
});
Change the relationship as follows..
In Form class
public function itemsForms()
{
return $this->hasMany('App\ItemsForm','form_id','id');
}
In ItemsForm
public function form()
{
return $this->belongsTo('App\Form', 'id','form_id');
}
You can use the relationship as
$res = Form::find($id);
foreach($res->itemsForms as $item)
{
echo $item->item_id;
}
I finally found out what the issue was.
The issue was I didn't include itemsForms in the visible array (even though it is not part of the database fields).
There was no particular reason to include the visible array in my case so I deleted it but if I had wanted to keep it I should have included itemsForms as an element of the array.

Laravel passport for different table than 'users'

I'm developing currently multi auth with Laravel Passport, so the app is gonna have users, and devices, and when i try to register with the devices it saves it to the devices database ,and if i try to login it gives me the Bearer token. But right now i want to get user middleware 'auth:api' or other way to get device information via token,but its seems that the tokens are stored in oauth_access_token table and with user_id .So is there a way to user laravel passport for another table except for users ? Thanks ?
Here is my code for Devices:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
use SMartins\PassportMultiauth\HasMultiAuthApiTokens;
class Device extends Authenticatable{
use Notifiable,HasApiTokens;
protected $fillable = [
'name', 'password' ,
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Device Controller :
<?php
namespace App\Http\Controllers;
use App\Device;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class DeviceController extends Controller{
//register
public function signupDevice(Request $request){
//cant registed with the same email twice
if(sizeof(Device::where('name','=',$request->query('name'))->get()) > 0)
return response()->json(['name has already been taken'],500);
$request->validate([
'name' => 'required|string',
'password' => 'required|string|confirmed']);
$device =new Device(
[
'name'=>$request->name,
'password'=>bcrypt($request->password)
]);
$device->save();
return response()->json([
'message' => 'Successfully created device!'
], 201);
}
public function login(Request $request){
//validate the data input
$request->validate([
'name' => 'required|string',
'password' => 'required|string',]);
//attempt returns true if the user is in the database
$credentials = request(['name', 'password']);
if(!Auth::guard('device')->attempt($credentials))
return response()->json([
'message' => 'Unauthorized'
], 401);
//get the device
$device = $request->user('device');
//create token PAT
$tokenResult = $device->createToken('Personal Access Token');
$token = $tokenResult->token;
if ($request->remember_me)
$token->expires_at = Carbon::now()->addWeeks(1);
//save the token
$token->save();
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString()
],200);
}
public function index(Request $request)
{
return response()->json($request->user());
}
}
Routes:
//routes for device auth
Route::group(
[
'prefix'=>'auth/device'
],function ()
{
Route::post('signup','DeviceController#signupDevice');
Route::post('login','DeviceController#login');
Route::group(
[
'middleware'=>'device'
],function(){
//all the routes that go throught middleware
Route::get('index','DeviceController#index');
});
});
Okay so As I was doing this myself I ended up in a roadblock:
The tokens registred in the database may end up with duplicate user_id, because your two tables will have different auto-increment values.
The token has a name property, so when a customer (in your case a device) that has the same user_id, you can differentiate them with the name.
To achieve what you are asking you need to declare another provider and another guard. It can be found at config/auth.php
<?php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'user-api'=>[
'driver'=>'passport',
'provider'=>'users',
'hash'=>false,
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
'customer-api'=>[
'driver'=>'passport',
'provider'=>'customers',
'hash'=>false,
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'customers'=>[
'driver'=>'eloquent',
'model'=>App\Customer::class,
],
],
...
the new model need to implement new properties/class to make it work:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
//
class Customer extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $guarded=['id'];
protected $fillable = [
/*fillable props*/
];
//
protected $hidden=[
/*Hidden props*/
];
//
protected $casts=[
/*Your casts*/
];
public function getAuthPassword()
{
return $this->password;
}
}
Now you need to prefix http request with the api middleware, inside App/Providers/RouteServiceProvider:
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
}
Then when you declare a route use the new auth middleware guard with the passport driver you declared:
Route::group( ['prefix' => 'customer','middleware' => ['auth:customer-api'] ],function(){
/*Authenticated staff route here*/
});
When you log in you should use the auth guard with the session driver:
public function customerLogin(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if($validator->fails()){
return response()->json(['error' => $validator->errors()->all()]);
}
if(auth()->guard('customer')->attempt(['email' => request('email'), 'password' => request('password')])){
config(['auth.guards.api.provider' => 'customer']);
$customer = Customer::select('customers.*')->find(auth()->guard('customer')->user()->id);
$success = $customer;
$success['token'] = $customer->createToken('Customer'.$customer->name,['customer'])->accessToken;
return response()->json($success, 200);
}else{
return response()->json(['error' => ['Email and Password are Wrong.']], 200);
}
}
Here is the token output table data for different authentication
{
"id": "2a8526b24bd89a47f29474a86ba350c843cd4f7c5b0785c34d908efe00a4715c43502dbd9f789b83",
"user_id": "19",
"client_id": "15",
"name": "user;SuperAdmin",
"scopes": "[\"user\"]",
"revoked": "0",
"created_at": "2021-02-24 02:10:31",
"updated_at": "2021-02-24 02:10:31",
"expires_at": "2021-02-25 02:10:31"
},
{
"id": "388792d1c191529c65f1fb67d58972d2b26aae19d99c8df1c2321ec100bedff96a38b7724626f1cb",
"user_id": "53",
"client_id": "15",
"name": "Customer;1First.1Last#example.com",
"scopes": "[\"customer\"]",
"revoked": "0",
"created_at": "2021-02-24 02:10:28",
"updated_at": "2021-02-24 02:10:28",
"expires_at": "2021-02-25 02:10:28"
}
I did not include scopes. But take note if you want to restrict route access you should implement them.
For example if your table name is tbl_user then you should create model as below and add model path inside config/auth.php
Model:
<?php
namespace App\model\Users;
use Illuminate\Database\Eloquent\Model;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class UserModel extends Authenticatable
{
use HasApiTokens, Notifiable;
protected $table = 'tbl_user';
public $timestamps = false;
}
config/auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\model\Users\UserModel::class,
],
],

Saving multiple records in a laravel eloquent create

I'm trying to save multiple records via
AppSettings::create(
[
'name' => 'mail_host',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_port',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_username',
'type' => $emailsettingstype->id,
'value' => '',
],
);
But from the above, only the first array is getting created. Where am i going wrong? Any help is appreciated.
I think this should do
AppSettings::createMany([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
Make sure you're passing an array of arrays, not a params of array.
UPDATE, you can use Model::insert() although according to what I've read, that method doesn't create/update the timestamps.
You can just use Eloquent::insert() link as below:
AppSettings::insert([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
The problem with above is that it won't update timestamps, find examples here
The Create many Method createMany is available on relationship check reference to this link and this documentation from laravel
so far my example look like this.
I have two models Pricing and AvailableService Model
Pricing Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Pricing extends Model
{
protected $fillable = ["name", "price"];
public function available(){
return $this->hasMany(AvailableService::class, "pricing_id", "id");
}
}
And the AvailableServiceMode look like this
namespace App;
use Illuminate\Database\Eloquent\Model;
class AvailableService extends Model
{
protected $fillable = ["pricing_id", "service_id"];
public function service(){
return $this->belongsTo(Service::class, "service_id", "id");
}
}
So createMany operation look like this
$insertMany = Pricing::create(['name'=>request('name')]);
$insertMany->available()->createMany([
['service_id'=>1],
['service_id'=>2],
['service_id'=>3],
['service_id'=>4],
['service_id'=>5],
]);
And it works for, you can give it a try too. THANKS
If you want to store multiple record in seeder use this method instead of insert because in my case I want to slug automatically created using spatie/laravel-sluggable pkg. If you used the insert or DB technique then you have to give the value for slug field also.
CategorySeeder
<?php
namespace Database\Seeders;
use App\Servcategory;
use Illuminate\Database\Seeder;
class CategorySeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$categories = [
[
'name' => 'Automotive',
// 'slug' => 'automotive',
],
[
'name' => 'Business Services',
// 'slug' => 'business-services',
],
[
'name' => 'Computer, Telecom & IT Services',
// 'slug' => 'computer-telecom-&-it-services',
],
[
'name' => 'Education & Training',
// 'slug' => 'education-&-training',
],
[
'name' => 'Finance',
// 'slug' => 'finance',
],
[
'name' => 'Hospitals, Clinic, Medical',
// 'slug' => 'hospitals-clinic-medical',
],
[
'name' => 'Real Estate, Construction, Property',
// 'slug' => 'real-estate-construction-property',
],
[
'name' => 'Travel,Toursim & Hotels',
// 'slug' => 'travel-toursim-&-hotels',
],
];
// Servcategory::insert($categories);
collect($categories)->each(function ($category) { Servcategory::create($category); });
}
}
In case some one searching for eloquent model, I used the following method:
foreach($arCategories as $v)
{
if($v>0){
$obj = new Self(); // this is to have new instance of own
$obj->page_id = $page_id;
$obj->category_id = $v;
$obj->save();
}
}
$obj = new Self(); is a must otherwise it only saves single record when $this is used.
in seeder create an array and do foreach with Model::create(). All your records will be with timestamps
protected $array = [
[...],
[...],
[...]
];
public function run()
{
foreach ($this->array as $value) {
Model::create($value);
}
}

Laravel 5.3 get boolean value from associative array

I have a ImageController like this:
$image = Image::where('id', $id)->first();
return [
'image' => $image,
'image_360' => $image['360']
];
The previous lines return to the browser the following:
{
"image": {
"id": 1,
"name": "default.jpg",
"360": 1,
},
"image_360": null
}
The Image migration:
$table->increments('id');
$table->string('name');
$table->boolean('360');
The Image model:
class Image extends Model
{
protected $fillable = ['name', '360'];
protected $casts = [
'360' => 'boolean'
];
}
Why $images['360'] returns null if its value is true?
Here is the workaround way: I've tryed many ways but havenot get a direct way to access the number value as descripted in this post
return [
'image' => $image,
'image_360' => array_values($image->toArray())[2];
];

Categories