Error trying to output the Eloquent relationship in Laravel - php

I'm getting the error "Trying to get property 'company_name' of non-object". I studied about the Eloquent relationship and try to implement in my code. But it gives me that error in the view (products.show)
Which part are wrong?
Is it okay to have many different relationship to other model as well?
In 'Vendor Model':
public function getRouteKeyName()
{
return 'roc_no';
}
public function user()
{
return $this->belongsTo('App\User');
}
public function products()
{
return $this->hasMany('App\Product');
}
In 'Product Model':
public function getRouteKeyName()
{
return 'slug';
}
public function user()
{
return $this->belongsTo('App\User');
}
public function vendor()
{
return $this->belongsTo('App\Vendor');
}
In 'User Model':
public function vendor()
{
return $this->hasOne('App\Vendor');
}
public function products()
{
return $this->hasMany('App\Product');
}
public function roles()
{
return $this->belongsToMany('App\Role', 'role_users');
}
In the 'products.show':
...
{!! $product->description !!}
<!-- The error is at $product->vendor->company_name -->
Company Name: {{ $product->vendor->company_name }}
In 'ProductController':
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|string|max:255',
'slug' => 'required|string|max:100',
'description' => 'required',
'image' => 'nullable',
]);
$product = new Product;
$product->name = $request->name;
$product->slug = $request->slug;
$product->description = $request->description;
$product->vendor_roc_no = auth()->user()->vendor->roc_no;
$product->save();
return redirect('/account/products')->with('success', 'Product added successfully.');
}
public function show(Product $product)
{
return view('products.show')->with('product', $product);
}
Updated:
In vendors table:
Schema::create('vendors', function (Blueprint $table) {
$table->increments('id');
$table->string('company_name');
$table->string('roc_no');
$table->string('company_address');
$table->string('company_tel');
$table->string('company_fax')->nullable();
$table->string('company_email');
$table->string('company_website')->nullable();
$table->string('company_logo')->nullable();
$table->text('company_profile')->nullable();
$table->unsignedInteger('user_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
In products table:
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug')->unique();
$table->text('description');
$table->string('image')->nullable();
$table->string('vendor_roc_no');
$table->timestamps();
// $table->foreign('vendor_id')->references('id')->on('vendors');
});

As far as I know, you'll need to establish the relationship for accessing properties from the other model, so in this case, you want to get the company_name that is on the vendor for this you'll need to tell your model to bring vendor. Example:
$user->with('anyRelation')->get();
// Then you can use like, $user->anyRelation->property;
Now I noticed something in your show method, you're sending Product class but not an eloquent object maybe just using Eloquent would help? Example
$products = Product:all(); // this would return every record on your db
return view('products.show')->with('products', $products);
I hope this helps :)

Related

Get all the genres that a movie belongs to in the Laravel REST API

Asside from the $movie data I want to also get all the genres that a movie belongs to following this path: 127.0.0.1:8000/api/movies/{id}. My foreign keys are in the separate table so how exactly I can achieve that? My migrations:
public function up()
{
Schema::create('genres', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
public function up()
{
Schema::create('movies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('status')->nullable()->default(0);
$table->string('image_path')->default('default.png');
});
}
public function up()
{
Schema::create('genre_movie', function (Blueprint $table) {
$table->foreignId('genre_id')->constrained()->cascadeOnDelete();
$table->foreignId('movie_id')->constrained()->cascadeOnDelete();
});
}
Movie model:
class Movie extends Model
{
use HasFactory;
public $timestamps = false;
protected $fillable = ['name', 'status', 'image_path'];
public function genres()
{
return $this->belongsToMany(Genre::class, 'genre_movie');
}
}
Genre model:
class Genre extends Model
{
use HasFactory;
public $timestamps = false;
protected $fillable = ['name'];
public function movies()
{
return $this->belongsToMany(Movie::class, 'genre_movie');
}
}
Movie Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'status' => $this->status,
'image_path' => url()->to('/images/' .$this->image_path),
];
}
Genre Resource:
public function toArray($request){
return [
'id' => $this->id,
'name' => $this->name,
];
}
Function in the controller which only returns $movie data:
public function show(Movie $movie)
{
return new MovieResource($movie);
}
I thought this would work:
public function show(Movie $movie)
{
return new MovieResource($movie->with('genres'));
}
But I receive this error: "message": "Property [id] does not exist on the Eloquent builder instance."
You can return JSON with a small change too.
public function showMovieAndGenre(Movie $movie)
{
$fullData = Movie::join('genres', 'genres.id', '=', 'movies.id')
->select([
'movies.id AS mid',
'genres.id AS gid',
'movies.name AS mname',
'genres.name AS gname',
'movies.status AS status',
'movies.image_path AS image_path'
])
->get();
return $fullData;
}
return $this->belongsToMany(Genre::class, 'genre_movie');
Consider using hasManyThrough https://laravel.com/docs/9.x/eloquent-relationships#has-many-through

Add multiple categories for articles

I need to add several categories for a new article. I will write down everything I do in order:
migration of categories
public function up()
{
Schema::create('blog_categories', function (Blueprint $table) {
$table->BigIncrements('id');
$table->string('title', 128);
$table->timestamps();
});
}
migration of articles
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->BigIncrements('id');
$table->string('title', 128);
$table->string('slug', 64);
$table->string('subtitle', 256)->nullable();
$table->timestamps();
});
}
creating another migration article_blog_category_table
public function up()
{
Schema::create('article_blog_category', function (Blueprint $table) {
$table->unsignedBigInteger('blog_category_id')->nullable();
$table->foreign('blog_category_id')
->references('id')->on('blog_categories')->onDelete('set null');
$table->unsignedBigInteger('article_id')->nullable();
$table->foreign('article_id')
->references('id')->on('articles')->onDelete('cascade');
});
}
Doing belongsToMany in models
article model
public function blog_categories()
{
return $this->belongsToMany('App\Models\BlogCategory');
}
category model
public function articles()
{
return $this->belongsToMany('App\Models\Article');
}
}
Next, I write the function for adding an article in the controller (I think there is no need to write the function for adding a category, everything is clear there)
Articles controller
public static function saveArticle(Request $request) {
$validator = Validator::make($request->all(), [
'blog_category_id' => 'required|numeric',
'title' => 'required|max:128',
'slug' => 'required|unique:articles|max:64',
'subtitle' => 'max:256',
]);
if ($validator->fails()) {
return response()->json([
'message' => $validator->errors()->first()
], 422);
}
$article = new Article();
$blog_category = BlogCategory::where('id', $request->blog_category_id)->first();
if(!$blog_category){
return response()->json([
'message' => 'Blog category not found'
], 404);
}
$article->blog_category_id = $request->blog_category_id;
$article->title = $request->title;
$article->slug = $request->slug;
$article->subtitle = $request->subtitle;
$article->save();
return Article::where('slug', $article->slug)->first();
}
I have a method in the function to add one category. The question of how to add here so that you can add several categories, I cannot figure it out. You need something like $article->blog_categories()->attach($request->blog_category_id); but how to apply it correctly?
Your naming convention is complicating your task.
Rename table in categories migration:
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->timestamps();
});
Also for simplicity, rename the joint (pivot) table
Schema::create('article_category', function (Blueprint $table) {
// You don't need a table id here
$table->foreignId('category_id')->index();
$table->foreignId('article_id')->index();
$table->unique(['article_id', 'category_id']);
// You also don't need timestamps
});
Defining relationships in the models:
// Article model
public function categories()
{
return $this->belongsToMany(\App\Models\Category::class);
}
// Category model
public function articles()
{
return $this->belongsToMany(\App\Models\Article::class);
}
Article controller
public function store() // No need to make the function static
{
$data = validator(request()->all(), [
// To save many categories, send them as an array
'categories' => 'array',
'categories.*' => [\Illuminate\Validation\Rule::exists('categories', 'id')], // Checks if category id is in the db
'title' => 'required|max:128',
'slug' => 'required|unique:articles|max:64',
'subtitle' => 'string',
])->validate();
$article = Article::create(
\Illuminate\Support\Arr::except($data, 'categories');
);
// Save categories
$article->categories()->attach($data['categories']);
return $article;
}
According to Documentation
Many To Many Relationships
Attaching / Detaching
//You can pass an array of id's categories in attach method:
$article->blog_categories()->attach($categories_ids_array);
/*
*if you want to pass more columns value you can pass an associative array of
*column names with their values e.g attach($categories ids array, an array of
*more columns with their values)
*/
$article->blog_categories()->attach($categories_ids_array, ['column_name' =>
$column_values]);

Laravel relationship return null

I have a store that is using a payment package
Now I want to show the items that were purchased, but I run into this problem
Controller
public function mycourse()
{
$courses = PurchasedCourse::where('user_id', Auth::id())
->with('course')
->get();
dd($courses);
return view('student.courses.mycourse', [
'courses' => $courses
]);
}
Model
public function course()
{
return $this->belongsTo(Course::class, 'id');
}
Migration
public function up()
{
Schema::create('courses', function (Blueprint $table) {
$table->id('id');
$table->string('name')->unique();
$table->string('start');
$table->string('end');
$table->integer('price');
$table->string('jalasat');
$table->string('zarfiat');
$table->text('tozih');
$table->integer('hit');
$table->string('department');
$table->string('thumbnail');
$table->tinyInteger('status')->default(0);
$table->string('slug')->unique();
$table->timestamps();
});
}
Your relationship method is wrong. The syntax for the belongsTo() method is
belongsTo(class, ?foreign_id, ?related_id). In your case, it should be:
public function course()
{
return $this->belongsTo(Course::class, 'course_id', 'id');
}
or just
public function course()
{
return $this->belongsTo(Course::class);
}
since your columns follow Laravel's naming conventions.

laravel migration changes not reflecting

i changed my post_id foreign key to posts_id in images migration in order for the relationship to work. however after changing, the insertion of images in images table is still picking post_id which is giving me an error column not found. i tried cache:clear and config but nothing worked. post_id is nowhere in my code
postcontroller
public function store( Request $request )
{
$data = request()->validate([
'user_id' => 'required',
'about' => 'required',
'category' => '',
'expire_date' => '',
]);
if (Auth::guard('web')->check())
{
$user = Auth::user();
$post = new Post();
/*$post = $user->posts()->create([
'about' => $data['about'],
'category' => $data['category'],
'expire_date' => $data['expire_date'],
]);*/
if($request->hasFile('image'))
{
$files = $request->file('image');
foreach($files as $file)
{
$name = time().'-'.$file->getClientOriginalName();
$name = str_replace('','-',$name);
echo $name;
$file->move('images',$name);
$post->images()->create(['image' => $name ]);
}
}
$user = Auth::guard('web')->id() ;
return redirect()->route('home',['user'=>$user]);
}
}
postmodel
public function posts()
{
return $this->belongsTo(User::class);
}
images model
class images extends Model
{
protected $fillable = [
'posts_id',
'image'
];
public function posts(){
return $this->belongTo(Posts::class);
}
}
posts migration
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->uuid('user_id')->nullable;
$table->uuid('admin_id')->nullable;
$table->string('category')->nullable;
$table->string('about');
$table->timestamps();
});
}
images migration
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('posts_id');
$table->string('image');
$table->timestamps();
$table->index('posts_id');
});
}
Try to change Images model
public function posts(){
return $this->belongTo(Posts::class);
}
to
public function posts(){
return $this->belongTo(Posts::class, 'posts_id');
}
Why would you change it? Laravel can handle this for you if leave it to post_id. Otherwise you’ll have to update the relation posts in your images model by adding extra arguments. See the documentation: laravel.com/docs/7.x/eloquent-relationships
public function posts(){
return $this->belongTo(Posts::class, 'posts_id');
}

Assign User to any Shop in Laravel Relationship?

In laravel, I have 3 table
User // for Authentication and Create Another User Once logged in
Expense
Shop
My Purpose- I want user can register and Also, can create another user when they logged in And Can assign user to another Shop as they want..
And Only User in the Same Shop Can see their Expense..
// My User Table
<pre>
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('shop_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
</pre>
// My Expense Table
<pre>
Schema::create('expenses', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->date('date');
$table->string('description');
$table->double('amount');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
</pre>
// My Shop Table
<pre>
Schema::create('shops', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('expense_id')->nullable();
$table->unsignedBigInteger('user_id');
$table->string('name');
$table->string('description');
$table->timestamps();
$table->foreign('expense_id')->references('id')->on('expenses');
$table->foreign('user_id')->references('id')->on('users');
});
</pre>
// My User Model
<pre>
public function expense()
{
return $this->hasMany(\App\Expense::class);
}
public function shop()
{
return $this->hasMany(\App\Shop::class, 'user_id');
}
</pre>
// My Expense Model
<pre>
class Expense extends Model
{
protected $fillable = ['date', 'description', 'amount', 'user_id', 'shop_id'];
public function user()
{
return $this->belongsTo(\App\User::class);
}
}
</pre>
// My Shop Model
<pre>
class Shop extends Model
{
protected $fillable = ['name', 'description', 'expense_id', 'shop_id'];
public function user()
{
return $this->belongsTo(\App\User::class, 'user_id');
}
}
</pre>
// Expense Controller
<pre>
public function index(Request $request)
{
$expense = Expense::with(['user'])->get();
return ExpenseResource::collection($expense);
// dd(auth()->user());
}
public function create(Request $request)
{
$request->validate([
'date' => 'required',
'description' => 'required',
'amount' => 'required',
]);
$expense = new Expense();
$expense->user_id = auth()->user()->id;
$expense->date = $request->date;
$expense->description = $request->description;
$expense->amount = $request->amount;
$expense->save();
return new ExpenseResource($expense);
}
</pre>
// in My UserController
<pre>
public function index()
{
$users = User::all();
$shops = Shop::all();
return view('user', compact('users', 'shops'));
// return UserResource::collection($users);
}
public function create(Request $request)
{
$request->validate([
'name' => 'required',
'email' => 'required',
'password' => 'required',
]);
$user = new user();
$user->user_id = auth()->user()->id;
$user->name = $request->name;
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
return new UserResource($user);
}
</pre>
Is it make sense?
Any idea, thanks..
As stated in the comments, you'll need to check the current User and constrain the returned Expense records to only those that 1) have a User and 2) match the same Store as the current User. This can be done in a single whereHas() clause:
public function index(Request $request) {
$user = auth()->user(); // If using default `Auth` logic.
$expenses = Expense::whereHas('user', function($subQuery) use($user){
return $subQuery->where('shop_id', '=', $user->shop_id);
})->with(['user'])->get();
return ExpenseResource::collection($expenses);
}
What ->whereHas() does is constrains the query fetching your Expense models to respect the logic you pass it, which in this case is only include Expense models that have a user that has the same shop_id as the currently logged in User.
Note:If the current User does not have a Shop, it might return unexpected results, but you could protect the route to only allow a User with a Shop to access it, etc.

Categories