How to filter collection in laravel? - laravel

I return the ExportTemplate model :
$exportTemplates = ExportTemplate::where('user_id', Auth::id())->get();
the result is something like:
[
{
"id": 45,
"name": "hallo",
"user_id": 1,
"deleted_at": null,
"created_at": "2020-02-27 14:12:50",
"updated_at": "2020-02-27 14:12:50",
"ExportTemplateColumns": [
{
"id": 398,
"export_template_id": 45,
"searchable_column_id": 7,
"created_at": "2020-02-27 14:12:50",
"updated_at": "2020-02-27 14:12:50",
"SearchableColumn": {
"id": 7,
"name": "bic",
"type": "string",
"searchable_table_id": 1,*** I need only results with searchable_table_id ==1
"created_at": "2020-02-06 09:11:24",
"updated_at": "2020-02-06 09:11:30",
"label": "BIC"
}
}
]
},
{
"id": 46,
"name": "fol",
(...)
I want to filter this result through Collection Filter to return only :
ExportTemplateColumns->SearchableColumn->searchable_table_id == 1
Models :
class ExportTemplate extends Model
{
protected $appends = ["ExportTemplateColumns"];
public function getExportTemplateColumnsAttribute()
{
return ExportTemplatesColumns::where('export_template_id',$this->id)->get();
}
public function user()
{
return $this->belongsTo(User::class,'id');
}
public function exportTemplatesColumns()
{
return $this->hasMany(ExportTemplatesColumns::class,'export_template_id');
}
}
class ExportTemplatesColumns extends Model
{
protected $appends = ["SearchableColumn"];
public function getSearchableColumnAttribute()
{
return SearchableColumn::find($this->searchable_column_id);
}
public function exportTemplate()
{
return $this->belongsTo(ExportTemplate::class,'id');
}
public function searchableColumn()
{
return $this->belongsTo(SearchableColumn::class,'id');
}
}

try this.
$exportTemplates = ExportTemplate::where('user_id', Auth::id())->get();
$filtered = $exportTemplates->filter(function($d){
return collect($d->ExportTemplateColumns)->filter(function($columns){
return $columns->SearchableColumn->searchable_table_id === 1;
})->count();
});
$filtered->all();

Related

Not getting expected data for Laravel JSON:API

I am trying to create an API based on the following two Models:
Product.php
<?php
namespace App\Models\Commerce;
use ...
class Product extends Model {
use HasFactory;
use SoftDeletes;
public mixed $id;
protected $fillable = ['user_id', 'title', 'default_variation', 'status'];
/**
+ * #return HasMany
+ */
public function productVariations() : HasMany {
return $this->hasMany('App\Models\Commerce\ProductVariation');
}
public function defaultVariation() : HasOne {
return $this->hasOne('App\Models\Commerce\ProductVariation', 'default_variation');
}
}
ProductVariation.php
<?php
namespace App\Models\Commerce;
use ...
class ProductVariation extends Model {
use HasFactory;
protected $fillable = ['user_id', 'product_id', 'sku', 'title', 'price', 'status'];
public function product() : BelongsTo {
return $this->belongsTo(Product::class);
}
}
I have configured the JSON:API according to the documents, as best I can tell:
routes/api.php
<?php
use ...
JsonApiRoute::server('v2')->prefix('v2')->resources(function ($server) {
$server->resource('products', JsonApiController::class)->readOnly();
$server->resource('product-variations', JsonApiController::class)->readOnly();
});
ProductSchema.php
<?php
// For authentication see ProductPolicy
namespace App\JsonApi\V2\Products;
use ...
class ProductSchema extends Schema
{
public static string $model = Product::class;
public function fields(): array
{
return [
ID::make(),
DateTime::make('createdAt')->sortable()->readOnly(),
DateTime::make('updatedAt')->sortable()->readOnly(),
Str::make('title'),
Number::make('user_id'),
Number::make('defaultVariation'),
HasOne::make('defaultVariation', 'default_variation'),
Boolean::make('status'),
HasMany::make('productVariations')
];
}
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}
ProductVariationSchema.php
<?php
namespace App\JsonApi\V2\ProductVariations;
use ...
class ProductVariationSchema extends Schema
{
public static string $model = ProductVariation::class;
public function fields(): array
{
return [
ID::make(),
DateTime::make('createdAt')->sortable()->readOnly(),
DateTime::make('updatedAt')->sortable()->readOnly(),
Number::make('userId'),
Number::make('productId'),
Str::make('sku'),
Str::make('title'),
Number::make('price'),
Boolean::make('status'),
BelongsTo::make('product')
];
}
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}
With some dummy data in the database, I can access the endpoint /api/v2/products and receive the following:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "products",
"id": "3",
"attributes": {
"createdAt": null,
"updatedAt": null,
"title": "foo-product",
"user_id": 1,
"status": 1
},
"relationships": {
"defaultVariation": {
"links": {
"related": "domain/api/v2/products/3/default-variation",
"self": "domain/api/v2/products/3/relationships/default-variation"
}
},
"productVariations": {
"links": {
"related": "domain/api/v2/products/3/product-variations",
"self": "domain/api/v2/products/3/relationships/product-variations"
}
}
},
"links": {
"self": "domain/api/v2/products/3"
}
}
]
}
/api/v2/product-variations
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "product-variations",
"id": "3",
"attributes": {
"createdAt": null,
"updatedAt": null,
"userId": 1,
"productId": 3,
"sku": "foo-bar",
"title": "foo bar ",
"price": "11.11",
"status": 1
},
"relationships": {
"product": {
"links": {
"related": "domain/api/v2/product-variations/3/product",
"self": "domain/api/v2/product-variations/3/relationships/product"
}
}
},
"links": {
"self": "domain/api/v2/product-variations/3"
}
}
]
}
However, if I try api/v2/products/3/product-variations or /api/v2/products/3/relationships/product-variations I get a 404 error:
{
"jsonapi": {
"version": "1.0"
},
"errors": [
{
"status": "404",
"title": "Not Found"
}
]
}
Additionally, if I run a test I also get a 404 but I do see that the variations are correctly related:
I think that somewhere I have misconfigured something, but this is my first time doing this in Laravel so I might be completely missing a configuration also.
You only set a JsonApiRoute (starting with api) and prefix('v2') meaning api/v2 group. In this group you add 2 more product-variations meaning /api/v2/product-variations and products /api/v2/products. So anything else don't exists and laravel returns 404.
You need to add
$server->resource('products/{id}/product-variations', JsonApiController::class)->readOnly();
for api/v2/products/3/product-variations
and
$server->resource('products/{id}/relationships/product-variations', JsonApiController::class)->readOnly();
for /api/v2/products/3/relationships/product-variations
Also you can add (int $id) in your controller methods to read this id you pass.

change hasManyThrough() relation attribute name through accessor

I have 3 Models
Campaign PK(id)
CampaignMedium FK(campaign_id)
AccountReceivable FK(campaign_medium_id) (has an amount column)
Controller function:
public function all()
{
return Campaign::with(['customer', 'receivedPayments'])->get();
}
In Campaign Model relationships are defined as follows:
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function accountReceivable()
{
return $this->hasManyThrough(AccountReceivable::class, CampaignMedium::class);
}
public function receivedPayments()
{
return $this->accountReceivable()
->selectRaw('sum(account_receivables.amount) as total')
->groupBy('campaign_id');
}
public function getReceivedPaymentsAttribute()
{
if (!array_key_exists('receivedPayments', $this->relations)) {
$this->load('receivedPayments');
}
$relation = $this->getRelation('receivedPayments')->first();
return ($relation) ? $relation->total : 0;
}
Final Output:
{
"data": [
{
"id": 8,
"name": "example",
"image": "campaign/90375849f6c3cc6b0e542a0e3e6295b890375849f6c3cc6b0e542a0e3e6295b8.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": [
{
"total": "700",
"laravel_through_key": 8
}
]
},
{
"id": 9,
"name": "example",
"image": "campaign/fff9fadc92a809513dc28134379851aafff9fadc92a809513dc28134379851aa.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": []
}
]
}
summary: trying to get the sum of AccountReceivable amount attribute, which is working fine but the getReceivedPaymentsAttribute() isn't working which needs to return the total value only. also can anyone please help me to explain why laravel_through_key is added with received_payments?
I've never tried to use an attribute modifier to modify a relation this way. You are overriding the expected result of receivedPayments(). You might be better off to define a separate attribute like so:
public function getSumReceivedPaymentsAttribute()
{
// ...your code...
}
Now you can access the attribute using $model->sum_received_payments or always preload it using:
// model.php
protected $appends = ['sum_received_payments'];

Laravel eloquent get related model as column_name => value

I have a Product table related with Image table, one to many relationship.
How do I get Image records not as array? Instead, return only 1 Image record as column_name: value
This code:
$products = $request->user()->products()->orderBy('id', 'desc')->with(['images' => function($query) {
$query->where('featured', 1)->limit(1);
}])->get();
Is returning data like this :
{
"id": 13,
"name": "Shoes",
"price": "3.00",
"stock": 5,
"pivot": {
"user_id": 7,
"product_id": 13
},
"images": [
{
"id": 5,
"file": "5da9d9b493403.png",
"featured" 1,
"pivot": {
"product_id": 13,
"image_id": 5
}
}
]
}
}
How to make it return like this?
{
"id": 13,
"name": "Shoes",
"price": "3.00",
"stock": 5,
"pivot": {
"user_id": 7,
"product_id": 13
},
"images": "5da9d9b493403.png"
}
}
You can append a custom accessor that gets the first image from the relationship
In the Product model
protected $appends = ['images'];
public function getImagesAttribute()
{
return $this->images()->where('featured', 1)->first()->file;
}
public function images()
{
return $this->hasMany('App\Image');
}
Then just return the query without eager loading
$products = $request->user()->products()->orderBy('id', 'desc')->get();
Hope this helps
In the Product model
protected $appends = ['images'];
public function images()
{
return $this->hasMany('App\Image')->select(['name'])->first();
}
select the name only and return first relation

Laravel access value from others object

I have 3 tables :-
available_offers
- id
- date
- time
- from
- to
- user_id
requested_offer
- id
- user_id
- available_offersID
user
- id
- name
- username
- password
- email
Here my routes_bookedController
class routes_bookedController extends Controller
{
public function create()
{
$user = available_offers::with('user')->get();
$booked = requested_routes::with('available_offers', 'user')
->where('user_id', Auth::id())
->get();
return $booked;
return view ('routes_booked.show', compact('booked'));
}
}
Here the result from show.blade.php from routes_bookedController
[
{
"id": 3,
"user_id": 4,
"available_offersID": 3,
"created_at": null,
"updated_at": null,
"available_offers": {
"id": 3,
"date": "2017-08-08",
"time": "14:11:30",
"from": "Sabah",
"to": "Sarawak",
"user_id": 2,
"isBooked": 1,
"created_at": null,
"updated_at": null
},
"user": {
"id": 4,
"name": "admin",
"username": "user1",
"email": "user1#example.com",
"created_at": "2017-08-08 06:15:49",
"updated_at": "2017-08-08 06:15:49"
}
}
]
Is that possible to grab name from available_offers->user->name ?
This is my model :-
requested_routes
class requested_routes extends Model
{
public function available_offers()
{
return $this->belongsTo(available_offers::class, 'available_offersID');
}
public function user()
{
return $this->belongsTo(User::class);
}
}
available_offers
class available_offers extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function requested_routes()
{
return $this->hasMany(requested_routes::class);
}
}
User
public function available_offers()
{
return $this->hasMany(available_offers::class);
}
public function requested_routes()
{
return $this->hasMany(requested_routes::class);
}
Eager load sub model
$booked = requested_routes::with('available_offers', 'available_offers.user', 'user')
->where('user_id', Auth::id())
->get();
Ensure you add available_offers.user as one of the with clauses.

Laravel's Eloquent pivot object

I have defined relation in my model:
public function positions() {
return $this->belongsToMany('Position', 'users_positions')->withPivot('season');
}
Is it possible to display objects with pivot in cleaner way?
For Example:
"positions": [
{
"id": 1,
"name": "A",
"pivot": {
"user_id": 1,
"position_id": 1,
"season": 2014
}
}
],
I would like to get:
"positions": [
{
"id": 1,
"name": "A",
"season": 2014
}
],
Use accessor:
public function getSeasonAttribute()
{
return ($this->pivot) ? $this->pivot->season : null;
}
Then you can access it just like other properties:
$position = $someModel->positions->first();
$position->season;
if you also need it in the toArray/toJson output, then use on you model:
protected $appends = ['season'];

Resources