Use the attribute of a model in a scope - laravel

I have defined an attribute in my model and want to use it in a scope.
protected $appends = ['lowest_price'];
public function getLowestPriceAttribute()
{
$lowest_price = NULL;
foreach($this->shops as $shop) {
if(is_null($lowest_price)) {
$lowest_price = (double)$shop->pivot->price;
}
if($lowest_price > (double)$shop->pivot->price) {
$lowest_price = (double)$shop->pivot->price;
}
}
return $lowest_price;
}
The lowest_price attribute I would like to use in a scope but it's always 'null':
public function scopeMaxPrice(Builder $query, $max_price): Builder {
return $query->where('max_price', '>=', $this->lowest_price);
}
If I dd(); the getLowestPriceAttribute function in the scope it also returns 'null'. In my view I have access to the 'lowest_price' attribute.
Can the attribute be part of the query? If not, is there an elegant solution?
EDIT: The scope gets called in the Product controller:
public function index()
{
$products = QueryBuilder::for(Product::class)
->allowedFilters(
AllowedFilter::exact('type_id')->default('1'),
AllowedFilter::scope('min_speed')->default(0),
AllowedFilter::scope('min_range')-> default(0)
# AllowedFilter::scope('max_price')-> default(999)
)
->get();
$types = DB::table('types')->pluck('name', 'id');
return view('pages.home', compact('products', 'types'));
}
Note: I am using the Spatie querybuilder

Related

Laravel Excel export

While exporting the data, it seemed to add the conditions for exporting. So, I made the constructor method and passed the data from the controller.
public function __construct($company_pan, $user_pan)
{
$this->company_pan = $company_pan;
$this->user_pan = $user_pan;
}
public function collection()
{
return PurchaseDetail::where([
['pan_code', $this->user_pan],
['s_pan', '=', $this->company_pan]
])->get();
}
In the Controller,
public function export(Request $request)
{
$company_pan = $request->pan;
$user_pan = Auth::user()->pan_code;
return Excel::download(new ConfirmationExport($company_pan, $user_pan), 'Confirmation.xlsx');
}
All the output is blank excel. But if I changed the code in the collection of s_pan as
public function collection()
{
return PurchaseDetail::where([
['pan_code', $this->user_pan],
['s_pan', '=', '303030333']
])->get();
}
It exports the data as expected. What actually the problem is?
Have you defined the attributes of your class? Like this?
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
class ExampleExport implements FromCollection
{
protected $company_pan;
protected $user_pan;
public function __construct($company_pan, $user_pan)
{
$this->company_pan = $company_pan;
$this->user_pan = $user_pan;
}
public function collection()
{
return PurchaseDetail::where([
['pan_code', $this->user_pan],
['s_pan', '=', $this->company_pan]
])->get();
}
}

Query returns an object instead of Array

I have written a model function which has to give me all the posts with user's id, but it returns a model object instead.
My model:
public static function my_posts(){
$userId = Auth::id();
$getPosts = DB::table('posts')->where('user_id',$userId)->get();
return $getPosts;
}
My Controller function:
public function myPosts(){
$getPosts = new posts_model();
$getPosts->my_posts();
return view("myposts",["posts" => $getPosts]);
}
How can I fix this error?
Can you please change Model
public static function my_posts(){
$userId = Auth::id();
$getPosts = Post::where('user_id',$userId)->get();
return $getPosts;
}
Then Change Your controller
public function myPosts(){
$getPosts = new posts_model();
$data = $getPosts->my_posts();
return view("myposts", ["posts" => $data]);
}

Laravel : Call to undefined method Illuminate\\Database\\Query\\Builder

On query get the error with undefine method. Simply i want to get data from two tables query look linke
public static function userDetail($id){
$result = User::whereHas('user_details', function ($query) {
$query->where('user_details.user_id',$id);
})->first();
return $result ;
}
Relationship
On Model User define relationship
public function userDetails()
{
return $this->hasOne(UserDetails::class);
}
and in userDetails model
public function user()
{
return $this->belongsTo(User::class);
}
just change
public static function userDetail($id){
$result = User::whereHas('user_details', function ($query) use($id) {
$query->where('user_id',$id);
})->first();
return $result ;
}
If you dont need to use static, you can use this $result value on userDetails
public function userDetail($id){
$result = userDetails::where('user_id', '=', $id)->first();
return $result ;
}
Then the result can get access to the user from the vale by using $value->user->{user property}

Add a filter function on laravel query builder

I am getting some data from a model like:
public function getExternalCodes()
{
return ExternalService::get()->pluck('add_id')->toArray();
}
Now before this I want to apply a function to filter the data by date:
public function getExternalCodes()
{
$query = ExternalService::get()->pluck('add_id')->toArray();
return $query->filterByDate($query, new ExternalServce);
}
My filter by date function
private function filterByDate(Builder $query, Model $model)
{
if (!request()->has('interval')) {
return $query;
}
$period = json_decode(request()->get('interval'));
if ($period->from) {
$query->where("{$model->getTable()}.created_at", ">=", Carbon::parse($period->from)->startOfDay());
}
if ($period->to) {
$query->where("{$model->getTable()}.created_at", "<=", Carbon::parse($period->to)->endOfDay());
}
return $query;
}
However I can not do it right now since I am calling get() before applying the filter. Any idea how to go about it ?
And yes dont add a question to filter with where since I can not do that
Since you're using Eloquent models, use a local scope for that:
private function scopeFilterByDate($query)
{
....
}
Then use the scope with:
public function getExternalCodes()
{
return ExternalService::filterByDate()->pluck('add_id')->toArray();
}

type casting in laravel json response in relationships eager loading

This is my post model.
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
protected $fillable = ['title','featuring_image', 'brief', 'body', 'seen_count'];
public function user(){
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function someComments()
{
return $this->comments()->limit(Constants::COMMENTS_COUNT_LIMIT);
}
public function commentsCount()
{
return $this->comments()
->selectRaw('post_id, count(*) as count')
->groupBy('post_id');
}
public function likes()
{
return $this->hasMany(Like::class);
}
public function isLiked()
{
return $this->likes()->where('user_id', auth()->check() ? auth()->user()->id : 0);
}
public function likesCount()
{
return $this->likes()
->selectRaw('post_id, count(*) as count')
->groupBy('post_id');
}
}
I executed this query on this model.
$post = Post::with(['categories', 'user', 'commentsCount', 'likesCount', 'isLiked'])->find($post->id);
Because of the relation between this table and like and comment table, The output of this query for 'commentsCount', 'likesCount', 'isLiked' is an array. But I need to receive numbers for 'commentsCount' and 'likesCount', and a boolean for 'isliked' as an output, in laravel josn response.
You might find it easier to use the withCount() the comes with Eloquent instead.
Then for is_liked you could use a scope to get the value and the cast it to a boolean:
public function scopeIsLiked($query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$relation = Relation::noConstraints(function () {
return $this->likes();
});
$q = $this->likes()->getRelationExistenceCountQuery(
$relation->getRelated()->where('user_id', auth()->check() ? auth()->user()->id : 0)->newQuery(), $query
);
$query->selectSub($q->toBase(), 'is_liked');
}
Please note you will need to add the use statement for Relation to the top of the class:
use Illuminate\Database\Eloquent\Relations\Relation;
You model could then look like:
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
protected $fillable = ['title', 'featuring_image', 'brief', 'body', 'seen_count'];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function someComments()
{
return $this->comments()->limit(Constants::COMMENTS_COUNT_LIMIT);
}
public function likes()
{
return $this->hasMany(Like::class);
}
/**
* Scope to add the "is_liked" flag.
*
* #param $query
*/
public function scopeIsLiked($query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$relation = Relation::noConstraints(function () {
return $this->likes();
});
$q = $this->likes()->getRelationExistenceCountQuery(
$relation->getRelated()->where('user_id', auth()->check() ? auth()->user()->id : 0)->newQuery(), $query
);
$query->selectSub($q->toBase(), 'is_liked');
}
}
And your query would look something like:
$post = Post::with('categories', 'user')
->withCount('likes', 'comments')
->isLiked()
->find($post->id);
Hope this helps!
You can use Laravel casts:
Inside the each model you can add the following to cast a value, per example:
protected $casts = [
'isLiked' => 'boolean',
];
Rwd's answer gives a nice solution using scopes, but for laravel 5.4+ you could get away with aliasing the withCount() result and then casting it to boolean with a $cast variable on the model or an accessor (with accessor, you can only get snake case is_liked). This way we don't need to write complex scopes.
The model would be
class Post extends Model
{
// rest of model
protected $casts = ['isLiked'=>'boolean'];
public function likes()
{
return $this->hasMany(Like::class);
}
}
Then in your controller
$post = Post::with('categories', 'user')
->withCount(
[
'likes as likesCount', 'comments as commentsCount',
'likes as isLiked' =>function($query){
$query->where('user_id', auth()->check() ? auth()->user()->id : 0);
}
]
)
->find($post->id);
And now you get likesCount (int), commentsCount (int) and isLiked (boolean)

Resources