Can someone explain how Laravel relationships work? - php

When I save a post it saves id of user which posted it.
That is great but I don't understand how.
Post controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
class PostController extends Controller
{
public function postCreatePost(Request $request)
{
$post = new Post();
$post->body = $request['body'];
$request->user()->posts()->save($post); //i cant understand this line
return redirect()->route('dashboard');
}
}
?>
User model
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements Authenticatable
{
use \Illuminate\Auth\Authenticatable;
public function posts()
{
return $this->hasMany('App\Post'); // ?
}
}
Post model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user()
{
return $this->belongsTo('App\User'); // ?
}
}
I don't understand how belongs to and has many relations work.
If someone could explain it to me I would be very grateful.

In this case, the hasMany indicates that it will find many rows with the user id on the user_id column in the posts table.
The belongsTo simply means that it will find a user with an id in the users table that matches the user_id column on the posts related row.
Finally, $request->user()->posts()->save($post);:
Get the request
Get the user from the request
Get the posts from the user
The ->save($model) is a bit tricker. This requires that the object passed to the ->save($object) function is an instance of the relationship. In our case, the relationship is posts() and therefore the $post simply has to be an instance of App\Post (or wherever your Models namespace is).

You can find an explanation in the official documentation and in the chapter "Eloquent Relationships" from the book "Code Smart" (Laravel 4).
You can find a lot more resources in Google.

Related

How to pass type and id to laravel polymorphic relation

I have a navigation model that can have many items associated with it:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Navigation extends Model
{
use HasFactory;
use Searchable;
protected $guarded = [];
public function navigation_items(): HasMany
{
return $this->hasMany(NavigationItem::class);
}
}
The navigation item model looks like this
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class NavigationItem extends Model
{
use HasFactory;
protected $guarded = [];
public function navigation(): BelongsTo
{
return $this->belongsTo(Navigation::class);
}
public function navigatable(): MorphTo
{
return $this->morphTo();
}
}
Now an item can either be of type Page or Blog, in this case the Page model looks like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use JetBrains\PhpStorm\ArrayShape;
use Laravel\Scout\Searchable;
class Page extends Model
{
protected $guarded = [];
public function navigatable(): MorphOne
{
return $this->morphOne(NavigationItem::class, 'navigatable');
}
}
When I try to save a navigation model and associate it with a item, the following error appears:
SQLSTATE[HY000]: General error: 1364 Field 'navigatable_type' doesn't have a default value
I save the model like this:
foreach ($this->selected as $id) {
$this->navigation->navigation_items()->create([
'navigation_id' => $this->navigation->id,
]);
Where $this->selected is the navigation id, it should automatically get the correct navigatable_type and navigatable_id, but this doesn't seem to be working.
passing in the type and id manually works, but this kinda defeats the point of a polymorphic relationship.
any ideas?
On NavigationItem model, since you defined polymorphic relation as 'navigatable' it is expected that NavigationItem model's table contains navigatable_type and navigatable_id. First please ensure this checks out.
Creating records through relation's base function is not a valid method. It is not clear what you are trying to achieve there but when you want to set relation there is two standard way of achieving it:
1- Associate
When a relation is defined as belongsTo, you may use associate() function. Like so:
$account = Account::find(10);
$user->account()->associate($account);
2- Attach
Attach is used when relation is defined belongsToMany (pivot). It allows you to attach multiple records to a model instance/record.
$user = User::find(1);
$user->roles()->attach($roleId);
So if you want to set a 'navigatable' to a Navigation instance, you may:
$somePageInstance=Page::find(55);
$nagivation->navigatable()->associate($somePageInstance)
$nagivation->save();//remember to save, otherwise it won't be

Get model data based on pivot table column

# TABLES:
# kids: name, birthday, active
# chores: name, start_age, number_of_kids_required
# chore_kid: chore_id, kid_id, chore_date
Kid.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Kid extends Model
{
public function chores(){
return $this->belongsToMany('App\Chore')
->withPivot('chore_date')
->withTimestamps();
}
}
Chore.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Kid;
class Chore extends Model
public function kids(){
return $this->belongsToMany('App\Kid')
->withPivot('chore_date')
->withTimestamps();
}
}
With belongs to many I can grab the chores with assigned kids and the pivot table having the requested chore date:
$chore->kids()->wherePivot('chore_date', $date);
How can I get all the chores with the same chore_date without limiting it to the kids that are assigned to a specific chore.
How do I do this? This is my guess at this but it doesn't work:
Chore::wherePivot('chore_date', $date);
I have tried other combinations, but can't seem to find the right syntax.
controller
$chore = Chore::whereHas('kids', function ($query) use ($date){
$query->where('chore_date','=', $date );
})->get();
with that you'll get chore with specific date. hope that help.

Laravel Eloquent Model change table name on runtime in a related instance

I am new to Laravel and also asked the question on Laracast without any success so far.
Here is my problem: I have a database layout something like this:
Table: categoryA_products
Table: categoryB_products
Table: categoryC_products
and per default the Laravel user table:
Table: user
I have create a two Laravel Eloquent models:
Product:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
// protected $table = '';
public function users()
{
return $this->belongsTo( User::class );
}
}
User:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function products()
{
return $this->hasMany( Product::class );
}
}
As each product has a different table name I would normally create 1 model for each table but as they are all similar I would like to define the model table name at runtime.
I know I can do this with "$product->setTable()" but as I use the "newRelatedInstance" class from Laravel (hasMany and belongsTo) I cannot initiate the product class and set the table.
Is there a workaround for this?
Yes, I am aware that I could create a category table and link the products to each category but this is a fictional database model. There is a reason for this approach and I can explain it more in detail if needed. That said it make sense for this sample but I cannot use it for the live database.
I have a working solution with a model for each "category" but this is very messy.
Any help would be appreciated.
Since you're unable to load the relations, you could try referencing and re-initializing them like:
$relations = $product->getEagerLoads();
$attributes = $product->getOriginal();
table_name = 'categoryA_products'; // or categoryB_products or categoryC_products
$product->newQuery()
->newModelInstance($attributes)
->setTable($table_name)
->setEagerLoads($relations)
->...

Laravel: How do I create the relationship and which one do I use?

I have the database structure that have a classes table, a users table and users_classes table that matches the other two, because a user can belong to multiple classes. I have a problem now. I have code like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Classes extends Model
{
protected $table = 'classes';
public function students()
{
}
}
And I want to be able to access the students of the class by typing $class = Classes::find(1) and then $class->students to access the students. How do I define the relationship without using the query builder? I want to use eloquent. Im a noobie in Laravel pls dont downvote.
You use a belongsToMany relation.
If your users_classes table has the fields user_id and class_id you can do the following:
public function students()
{
return $this->belongsToMany(Student::class, 'users_classes', 'class_id', 'user_id');
}

Laravel merging relationships tables

I have two models (and two tables in the database) in Laravel 5: Artwork and Option (table names are artworks and options). A single artwork can have many options, so my models are as follows:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Artwork extends Model {
public function options(){
return $this->hasMany('App\Option');
}
}
and
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Option extends Model {
//
}
I'm trying to output an array that will contain ALL the artworks that will have their options nested inside them. Outputting only artworks works fine ($artworks = $artwork->get() as shown below), but I cannot figure out how to merge / nest options within each artwork.
Here is my Route for this
get('api/artworks','ArtworksController#index');
And here is my controller
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Artwork;
class ArtworksController extends Controller {
public function index(Artwork $artwork)
{
$artworks = $artwork->merge($artwork->options())->get();
return $artworks;
}
}
I was playing around with that merge() function and I just couldn't get it right. Is it possible to achieve this with merge() at all?
You just need to eager load the options relationship:
$artworks = $artwork->with('options')->get();

Categories