Laravel 5.1 using morph relation with global scope - laravel

I have global scope on my Product model and method withChildren to get data over scope. All was ok, until i tried use it with morph relation.
Code
Scope code
public function apply(Builder $builder, Model $model)
{
return $builder->whereNull('parent_id');
}
/**
* Remove the scope from the given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function remove(Builder $builder, Model $model)
{
$query = $builder->getQuery();
foreach ((array) $query->wheres as $key => $where)
{
if($where['column'] === 'parent_id')
{
unset($query->wheres[$key]);
$query->wheres = array_values($query->wheres);
}
}
}
withChildren method
public function scopeWithChildren()
{
return with(new static)->newQueryWithoutScope(new ParentScope);
}
Scope injected in model through boot method, like so
protected static function boot()
{
parent::boot();
//exclude children products from all results by default
Product::addGlobalScope(new ParentScope);
}
Problem
Relation returns null before i can implement my withChildren method. Invoice and Product have simple plymorphic relation.
$products = $invoice->products; //products is null, because of global scope
Tried
$invoice->products()->withChildren()->get() //500 error without any description
$invoice->with('products', function($q) {$e->withChildren();})->get(); //explode() expects parameter 2 to be string, object given

Related

Get nested relationship where use belongs to page and has the correct roles

So, I am trying to check if a user belongs to a page that owns a job.
The relationships are like so:
user.php
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function pages(): BelongsToMany
{
return $this->belongsToMany(Page::class);
}
page.php
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function jobs(): HasMany
{
return $this->hasMany(Job::class);
}
job.php
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function page(): BelongsTo
{
return $this->belongsTo(Page::class);
}
Now, I want to see if a user belongs to a page that owns the job:
$job = Job::find(1); //belongs to page_id 1, has job_id 1
$user = User::find(1); // only owns 1 page and 1 job.
I can change my Job::find(1) to any idea, and the code below will always return true, even if User::find(1) doesn't belong to a page that owns that job.
The $job variable exists, and is a eloquent model for the Job.
return $job->page->whereHas('users', function($query) use($user) {
$query->where('page_user.user_id', $user->id);
$query->whereIn('page_user.role', ['admin', 'editor']);
})->exists();
What am I doing wrong?
The problem with your code is this part: $this->page->whereHas(...). $this->page is an Eloquent Collection and you're treating it like a Query Builder. To append a query you need to call the relationship method, in this case page() like in the example in the documentation.
return $job->page()
->whereHas('users', function ($query) use ($user) {
$query->where('page_user.user_id', $user->id);
$query->whereIn('page_user.role', ['admin', 'editor']);
})
->exists();

Laravel 6 - Accessor appends not called in relationships

I use Laravel 6 and I want access to avatar attribute from user when I use posts() relation.
User model:
/**
* #var array
*/
protected $appends = [
'avatar',
];
/**
* #return HasMany
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
/**
* #return string
*/
public function getAvatarAttribute(): string
{
return sprintf('https://secure.gravatar.com/avatar/%s?s=500', md5($this->email));
}
The code of my controller:
$topic = Topic::where('slug', $slug)->firstOrFail();
foreach ($topic->posts()->get() as $post) {
dd($post->user->avatar); // return null
}
Post model:
/**
* #return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
I get the name of user with $post->user->name but avatar attribute is not called.

Laravel Relationship Find UUID

I have make a Trait for UUID. I use a lot of relationschip inside my code. On a relationship you can do find() and findOrFail() but i have write a code for findU() and findUOrFail() but i can't use it inside a relationship. How can i fix it?
Trait:
<?php
namespace App\Modules\Base\Traits;
use Ramsey\Uuid\Uuid;
/**
* Trait Uuids
*
* #package Modules\Core\Traits
*/
trait Uuids
{
/**
* Boot function from laravel.
*/
public static function bootUuids ()
{
static::creating(function ($model) {
$model->uuid = Uuid::uuid4()->toString();
});
}
/**
* #param $uuid
*
* #return mixed
*/
public static function findU ($uuid)
{
return static::where('uuid', '=', $uuid)->first();
}
/**
* #param $uuid
*
* #return mixed
*/
public static function findUOrFail($uuid)
{
$post = static::where('uuid', '=', $uuid)->first();
if( is_null($post) ) {
return abort(404);
} else {
return $post;
}
}
}
Controller:
/**
* Show
*/
public function show(Request $request, $uuid)
{
return responder()->success($request->user()->projects()->findUOrFail($uuid))->respond();
}
Error:
Call to undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany::findUOrFail()
Assuming you don't need id since you're using uuid
In your migration file you need:
$table->uuid('uuid');
$table->primary('uuid');
In your model:
use Uuids;
protected $primaryKey = 'uuid';
public $incrementing = false;
Or much easier
In your migration file:
$table->uuid('id');
$table->primary('id');
In your model:
use Uuids;
public $incrementing = false;
You don't need to override findOrFail or find
It should help to have the function referenced directly in the model rather than trying to access it directly in a trait. I am assuming that you are including the Uuids trait above in your projects model. If so, try creating a method on the projects model like this:
public function tryFindUOrFail($uuid)
{
return $this->findUOrFail($uuid);
}
Then you would write your show method as:
return responder()->success($request->user()->projects()->tryFindUOrFail($uuid))->respond();
If this doesn't work, you may need to include your method with the $appends array so that it is directly accessible through the relationship.

Busting cache on relation with Laravel 5.6

I have a model named Tournament where each Tournament is cached with some of its relations, using a key for each model (i.e. tournament.1).
return \Cache::remember('tournament.' . $id, 60*24*7, function() use ($id) {
return Tournament::where('id', $id)
->with(['prizes', 'sponsor'])
->firstOrFail();
});
When I update on the relations, I would like to forget that tournament's key. I know I could use event like this:
public static function boot()
{
static::saving(function ($prize) {
\Cache::forget('tournament.' . $prize->tournament->id);
});
return parent::boot();
}
However, doing this means I have to repeat this code for all other relations as well. I could probably create a trait for this, but is there a better way of doing what I want to achieve?
I ended up solving this using a trait.
namespace App\Traits;
trait ShouldCacheBust
{
/**
* The "booting" method of the model.
*/
public static function boot()
{
static::saving(function ($model) {
$cacheKey = static::cacheBustKey($model);
if ($cacheKey) {
\Cache::forget($cacheKey);
}
});
return parent::boot();
}
/**
* Return the key to be removed from Cache
*
* #param Model $model
* #return string|null
*/
abstract public function cacheBustKey(Model $model);
}
Then using it like this:
/**
* Return the key to be removed from Cache
*
* #param Model $model
* #return mixed|string
*/
public static function cacheBustKey(Model $model)
{
return 'tournament.' . $model->id;
}

Laravel Trying to get property of non-object

I am struggling to understand how laravel works and I have a very difficult time with it
Model - User.php the User model
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $fillable = array('email' , 'username' , 'password', 'code');
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
public function Characters()
{
return $this->hasMany('Character');
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
}
Model - Character.php the character model
<?php
class Character extends Eloquent {
protected $table = 'characters';
protected $fillable = array('lord_id','char_name', 'char_dynasty', 'picture');
public function user()
{
return $this->belongsTo('User');
}
public function Titles()
{
return $this->hasMany('Title');
}
}
?>
routes.php
Route::group(array('prefix' => 'user'), function()
{
Route::get("/{user}", array(
'as' => 'user-profile',
'uses' => 'ProfileController#user'));
});
ProfileController.php
<?php
class ProfileController extends BaseController{
public function user($user) {
$user = User::where('username', '=', Session::get('theuser') );
$char = DB::table('characters')
->join('users', function($join)
{
$join->on('users.id', '=', 'characters.user_id')
->where('characters.id', '=', 'characters.lord_id');
})
->get();
if($user->count()) {
$user = $user->first();
return View::make('layout.profile')
->with('user', $user)
->with('char', $char);
}
return App::abort(404);
}
}
In my code I will redirect to this route with the following:
return Redirect::route('user-profile', Session::get('theuser'));
In the view I just want to do:
Welcome back, {{ $user->username }}, your main character is {{ $char->char_name }}
My problem is that I will receive this error: Trying to get property of non-object in my view. I am sure it is referring to $char->char_name. What's going wrong? I have a very difficult time understanding Laravel. I don't know why. Thanks in advance!
You should be using the Auth class to get the session information for the logged in user.
$user = Auth::user();
$welcome_message = "Welcome back, $user->username, your main character is $user->Character->char_name";
You don't need to pass anything to that route either. Simply check if the user is logged in then retrieve the data. You have access to this data from anywhere in your application.
if (Auth::check())
{
//the user is logged in
$user = Auth::user();
To answer your question in the comments, reading the documentation would solve all of these problems, however:
public function user()
{
if (Auth::check())
{
$user = Auth::user();
return View::make('rtfm', compact('user'));
}
else
{
return "The documentation explains all of this very clearly.";
}
}

Resources