Get property of relation in model - laravel

class Ingredient extends Eloquent {
public function unit() {
return $this->hasOne('IngredientUnit', 'id', 'unit_id');
}
}
class IngredientUnit extends Eloquent {
public function ingredient() {
return $this->belongsTo('Ingredient', 'unit_id', 'id');
}
public function getNamesAttribute() {
$quantity = $this->ingredient()->quantity; // <- ErrorException
...
}
}
ErrorException (E_UNKNOWN):
Undefined property: Illuminate\Database\Eloquent\Relations\BelongsTo::$quantity
If I remove brackets - $this->ingredient->quantity; - I get
ErrorException (E_UNKNOWN)
Trying to get property of non-object
How can I get property of relation object (belongsTo) ?
Schemas:
Schema::create('ingredients', function(Blueprint $table)
{
$table->increments('id');
$table->integer('recipe_id')->unsigned();
$table->integer('unit_id')->unsigned()->nullable();
$table->float('quantity')->unsigned();
...
});
Schema::create('ingredient_units', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
...
});

With these tables your relations are wrong.
You need to swap them, for it goes like this: unit hasOne/hasMany ingredients, ingredient belongsTo unit.
// Ingredient
public function unit()
{
return $this->belongsTo('IngredientUnit', 'unit_id', 'id');
}
// IngredientUnit - I don't think it's hasOne, rather hasMany
public function ingredients()
{
return $this->hasMany('Ingredient', 'unit_id', 'id');
}
Next, this can't work:
$this->ingredient()->quantity;
but this will work, as long as there is model returned from the relation:
$this->ingredient->quantity;
So basically you don't have related ingredient, that's why it returns null and your get the error.

First you should change:
$quantity = $this->ingredient()->quantity;
into
$quantity = $this->ingredient->quantity;
But in case no Ingredient is found, you get exception, so you should better change it into:
$ingredient = $this->ingredient;
$quantity = ($ingredient) ? $ingredient->quantity : 0;
to assign 0 if no ingredient is found;

Related

How can I with pivot table get related creator of table?

In laravel 9 app I create many top many relation with table
return new class extends Migration {
public function up()
{
Schema::create('article_vote', function (Blueprint $table) {
$table->id();
$table->foreignId('article_id')->references('id')->on('articles')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->foreignId('vote_id')->references('id')->on('votes')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->boolean('active')->default(false);
$table->date('expired_at')->nullable();
$table->integer('supervisor_id')->nullable()->unsigned();
$table->foreign('supervisor_id')->references('id')->on('users')->onDelete('CASCADE');
$table->mediumText('supervisor_notes')->nullable();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable();
$table->unique(['vote_id', 'article_id'], 'article_vote_vote_id_article_id_index');
$table->index(['vote_id', 'article_id', 'active', 'expired_at'], 'article_vote_vote_id_article_id_active_expired_at_index');
$table->index([ 'expired_at', 'active',], 'article_vote_expired_at_active_index');
$table->index(['created_at'], 'article_vote_created_at_index');
});
Artisan::call('db:seed', array('--class' => 'articleVotesWithInitData'));
}
In app/Models/Vote.php I also have creator method :
public function articles(): BelongsToMany
{
return $this->belongsToMany(Article::class, 'article_vote', 'vote_id')
->withTimestamps()
->withPivot(['active', 'expired_at', 'supervisor_id', 'supervisor_notes']);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'creator_id', 'id');
}
and in app/Models/Article.php :
public function votes(): BelongsToMany
{
return $this->belongsToMany(Vote::class, 'article_vote', 'article_id')
->withTimestamps()
->withPivot(['active', 'expired_at', 'supervisor_id', 'supervisor_notes']);
}
Getting votes by article I also would like to get related creator of any vote li8ke :
$articleVotes = $article->votes->with('creator');
But I got error:
BadMethodCallException: Method Illuminate\Database\Eloquent\Collection::with does not exist. in /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Macroable/Traits/Macroable.php:113
Can I do it in some or other way ?
Thanks!
I think you need to change the relationship method for load creator in votes.
For that you need to change your code to :
$articleVotes = $article->votes;
$articleVotes->load('creator');

Completion system with Laravel + VueJS

I've been tring for hours to define my relations for a completion system but I failed.
I have a table Users and a table Episodes, and I would like to get in my views if the User has completed an episode.
I created a "completions" table with user_id and episode_id and a boolean field called "completed"
Is it a manytomany relation ? I'd like to have something like $episode->completed which gave me True if the logged in user finished the course, but I can't find my way... I just wanna know how to define my relations, not a whole work done.
Thank you very much !!!!
I believe you can tell Laravel what table to use and also query the pivot column.
//user model
public function episodes(){
return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' );
}
public function completedEpisodes(){
return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' )
->wherePivot('completed','=', true)->get();
}
//episode model
public function users(){
return $this->belongsToMany( 'App\User', 'completions', 'episode_id', 'user_id' );
}
The alternative would be to create your pivot as episode_user and laravel will auto detect it as the pivot, add a completed boolean to that table and it would function with just:
//user model
public function episodes(){
return $this->belongsToMany('App\Episode');
}
public function completedEpisodes(){
return $this->belongsToMany('App\Episode')
->wherePivot('completed','=', true)->get();
}
//episode model
public function users(){
return $this->belongsToMany('App\User');
}
Query if episode is complete:
//maybe, haven't tried this
//user
public function hasCompletedEpisode($id){
return $this->belongsToMany('App\Episode')->wherePivot('episode_id','=', $id)
->wherePivot('completed','=', true)->get();
}
//episode
public function hasCompletedEpisode($id){
$user_id = Auth::id();
return $this->belongsToMany('App\User')->wherePivot('user_id','=', $user_id)
->wherePivot('completed', true)->get();
}
If I was you, I would use a custom intermediate table. You can implement this as follows:
Migrations
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('episodes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('watches', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('epsiode_id');
$table->boolean('completed');
$table->timestamps();
});
Models
class User extends Model
{
protected $guarded = [];
public function watchedEpisodes()
{
return $this->hasMany(Episode::class)->using(Watch::class);
}
public function watches()
{
return $this->hasMany(Watch::class);
}
}
class Episode extends Model
{
protected $guarded = [];
public function users()
{
return $this->hasMany(User::class)->using(Watch::class);
}
}
class Watch extends \Illuminate\Database\Eloquent\Relations\Pivot
{
protected $table = 'watches';
protected $guarded = [];
public static function create(User $user, Episode $episode, bool $completed = false)
{
$watch = new self();
$watch->user_id = $user->id;
$watch->epsiode_id = $episode->id;
$watch->completed = $completed;
$watch->save();
return $watch;
}
public function user()
{
return $this->hasOne(User::class);
}
public function episode()
{
return $this->hasOne(User::class);
}
}
Example Use
$user = User::create(['name' => 'Big Watcher']);
$episode1 = Episode::create(['name' => 'Episode 1']);
$episode2 = Episode::create(['name' => 'Episode 2']);
$episode3 = Episode::create(['name' => 'Episode 3']);
$episode4 = Episode::create(['name' => 'Episode 4']);
Watch::create($user, $episode1);
Watch::create($user, $episode2);
Watch::create($user, $episode3);
return $user->watchedEpisodes;

Laravel : get the top 10 users from answer's survey table

I want to display the top 10 users who are answering for the surveys
I tried this
public function topuser()
{
$bestuser = Answer::whereRaw('id = (select max(count(`id`)) from Answer)')->get();
return view('dashboard.top')->with('bestuser', $bestuser);
}
But it gives me an error.
Answer model:
class Answer extends Model
{
protected $fillable = ['answer'];
protected $table = 'answer';
public function survey() {
return $this->belongsTo(Survey::class);
}
public function question() {
return $this->belongsTo(Question::class);
}
}
Answer Migrate file :
public function up()
{
Schema::create('Answer', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('question_id');
$table->integer('survey_id');
$table->string('answer');
$table->timestamps();
});
}
How to fix that, please?
If you are looking for top users (those with the most posts), it would probably be easier to come from the User model angle. Thus, pull a count from the Answer relationship on the User model, something like this:
$bestuser = User::withCount('answers as answer_count')
->orderBy('answer_count', 'desc')
->take(10)
->get();
Or if you just want a simple list:
$bestuser = User::withCount('answers as answer_count')
->orderBy('answer_count', 'desc')
->take(10)
->pluck('answer_count', 'name');
you can do like this
public function topuser()
{
$bestuser = Answer::OrderBy('created_at', 'DESC')->take(10)->get();
return view('dashboard.top')->with('bestuser', $bestuser);
}

Laravel return value of a specific column in a relationship

I am extremely new to Laravel. You could help me with a small problem:
I can not return in a collection, only the value of a specific column in a relation defined in the model. I will explain:
I have 2 tables:
1 - Tomos
2 - Documents
Migrations:
1- Tomos
private $table = 'tomos';
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create($this->table, function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable(false);
$table->text('description')->nullable(true);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
2- Documents
private $table = 'documents';
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create($this->table, function (Blueprint $table) {
$table->increments('id');
$table->date('date')->nullable(false);
$table->integer('provider_id');
$table->integer('tomo_id');
$table->string('folio')->nullable(false);
$table->integer('user_id');
$table->text('description');
$table->timestamps();
$table->foreign('provider_id')
->references('id')->on('providers');
$table->foreign('tomo_id')
->references('id')->on('tomos');
$table->foreign('user_id')
->references('id')->on('users');
});
}
Relations
1- Tomo
public function document()
{
return $this->hasMany(Document::class);
}
2- Document
public function tomo()
{
return $this->belongsTo(Tomo::class);
}
Controller
class Documents extends Controller
{
public function read(Request $request)
{
$provider = $request->only('id');
$documents = Document::select(['id', 'date', 'tomo_id', 'description'])
->with([
'tomo' => function ($query) {
$query->select('id', 'name');
}
])->orderBy('date', 'ASC')
->paginate(25);
return $documents;
}
}
I'm getting the following response in JSON:
current_page 1
data […]
0 {…}
id 2
date 2018-12-01
tomo_id 1
description 1
tomo {…}
id 1
name Tomo 1
But ... I do not want the key ('tomo') to return an object, I want it to return the value of the column ('name') as a string. Example:
current_page 1
data […]
0 {…}
id 2
date 2018-12-01
tomo_id 1
description 1
tomo Tomo 1
Thank you very much in advance..
First you need to add protected $appends = array('tomo_name'); as attribute, because this is one that doesn't exist on the model table.
public function getTomoNameAttribute()
{
return $this->tomo()->name;
}
After this, you can access the tomo name like this ->tomo_name
I'm not 100% sure that this code will work with just copy paste, but you might get the idea and work on it a little bit more.
Oh and be aware that loading the attribute, will query the database for that "tomo" every time.
Thank you very much to: Peter and Munteanu Petrisor
In special to : Munteanu Petrisor
I have been able to solve my problem with the solution that you propose to me, previously I had achieved it using 'join':
class Documents extends Controller
{
public function read(Request $request)
{
$provider = $request->only('id');
$documents = Document::join('tomos', 'documents.tomo_id', '=', 'tomos.id')
->join('users', 'documents.user_id', '=', 'users.id')
->where(['provider_id' => $provider])
->paginate(25, array(
'documents.id',
'documents.date',
'documents.folio',
'documents.description',
'tomos.name as tomo',
));
return $documents;
}
}
And now with your help, using attributes works wonders:
Document Model
protected $appends = [
'tomo_name',
'user_fullname'
];
public function getTomoNameAttribute()
{
return $this->tomo()->first()->name;
}
public function getUserFullNameAttribute()
{
return $this->user()->first()->first_name . ' ' . $this->user()->first()->last_name;
}
Document Controller
class Documents extends Controller
{
public function read(Request $request)
{
$provider = $request->only('id');
$documents = Document::select(['id', 'date', 'tomo_id', 'user_id', 'folio', 'description'])
->where(['provider_id' => $provider])
->orderBy('date', 'ASC')
->paginate(25);
return $documents;
}
}
And now it returns the data the way I expected
data […]
0 {…}
id 2
date 2018-12-01
tomo_id 1
user_id 1
folio 1
description 1
tomo_name 1
user_fullname First Last
Thank you so much!
try this
class Documents extends Controller
{
public function read(Request $request)
{
$provider = $request->only('id');
$documents = Document::select(['id', 'date', 'tomo_id', 'description'])
->with('tomo:id,name') // get relationship with id and name
->orderBy('date', 'ASC')
->paginate(25);
return $documents;
}
}
In your controller, try:
$documents->getCollection()->transform(function ($item) {
$item->tomo = $item->tomo->name;
return $item;
});
return $documents;

How do I add a new record ith belongsToMany and hasMany relationships using a pivot table?

I just setup model Asset with belongsToMany rooms and model Room with hasMany assets relationships. I also created a pivot table that stores both the ids of room and assets.
MODELS
class Asset extends Model
{
protected $fillable = [ 'name', 'slug', 'price' ];
public function room()
{
return $this->belongsToMany('App\Room');
}
}
class Room extends Model {
protected $fillable = [ 'number', 'status' ];
public function assets()
{
$this->hasMany('App\Asset'); }
}
}
MIGRATIONS
public function up()
{
Schema::create('assets', function (Blueprint $table)
{
$table->increments('id');
$table->string('name')->unique();
$table->string('slug');
$table->string('price');
$table->timestamps();
$table->softDeletes();
});
}
public function up()
{
Schema::create('asset_room', function(Blueprint $table) {
$table->integer('asset_id')->unsigned();
$table->foreign('asset_id')->references('id')->on('assets');
$table->integer('room_id')->unsigned();
$table->foreign('room_id')->references('id')->on('rooms');
$table->unique(['asset_id', 'room_id']);
$table->timestamps();
});
}
I added an asset via php artisan tinker as:
$asset = new App\Asset;
$asset->name = "Lamp";
$asset->slug = "lamp";
$asset->price = 40;
$asset->save();
Now how do I add an asset to a room so that it also adds an entry to the pivot table?
$room = new App\Room;
$room->number = 1;
$room->status = 1;
$room->asset...?
$room->save();
UPDATE
On Alexey's suggestion, I've updated the following function in Room modal since it's a belongsToMany and not a hasMany relationship.
public function assets()
{
$this->belongsToMany('App\Asset');
}
I created an asset with id 1. When trying to attach asset to room as:
$room->assets()->attach(1);
I get the following error:
PHP Fatal error: Call to a member function attach() on null in C:\xampp\htdocs\hotel\vendor\psy\psysh\src\Psy\ExecutionLoop‌​\Loop.php(90) : eval()'d code on line 1
which also breaks the Tinker mode (Psy Shell) execution.
First of all, since it's a many-to-many relationship, it should be belongsToMany():
public function assets()
{
return $this->belongsToMany('App\Asset');
}
To attach an asset to a room, use attach() method:
$asset = new App\Asset;
$asset->name = "Lamp";
$asset->slug = "lamp";
$asset->price = 40;
$asset->save();
$room = new App\Room;
$room->number = 1;
$room->status = 1;
$room->save();
$room->assets()->attach($asset->id);

Resources