Laravel8 Model Injection in Controller with Relationship - laravel

I have 2 models: Company and Category
Company has many Categories, and Category belongs to Company
// App\Models\Company.php
public function categories()
{
return $this->hasMany(Category::class);
}
and
// App\Models\Category.php
public function company()
{
return $this->belongsTo(Company::class);
}
All ok with relationship, now I have a route
/company/{company}/category/{category}
And in the controller I have the next code:
public function foo(Company $company, Category $category)
{
///
}
And I get a 404 not found. I think it is because the relationship between Company and category fails.
If I do the next:
public function foo($company, Category $category)
{
dd(
Company::find($company)->categories()->where('id', $category->id)->first(),
$category
);
}
I get the correct Category in both way, so, the relationship in model I guess is ok. Also, I can add/remove with relationship, so again, I guess is ok.
Now, my question is, how I can inject 2 model with relationship in the controller?
Cuz all way I'm getting error 404.
Thanks!

Related

Laravel Resource Call Relationship from other Relationship

is there a way to get relationship of other relationship in Laravel resource?
I try something like this:
'customers' => $this->customers()->files()->get(),
I am actually inside Products. Products hasMany Customers and also Customers hasMany Files. What I'm trying to do is to get files of customer. Is there a solution for this?
Try
$this->customers()->with('files')->get()
Should work if proper relationships are setup.
EDIT:
The simplest solution to get count as well will be
$this->customers()->with('files')->withCount('files')->get()
Assuming these are your models
class Products extends Model
{
public function customers(){
return $this->hasMany(Customers::class, 'product_id');
}
}
class Customers extends Model
{
public function files(){
return $this->hasMany(Files::class, 'customer_id');
}
}
To access all the files of one customer ($this is product)
$customers = $this->customers;
foreach($customers as $customer){
$file = $customer->files;
}
If you want to access all the files of all the customers you can add another method to your Product model like below and $files = $product->files
class Products extends Model
{
public function customers(){
return $this->hasMany(Customers::class, 'product_id');
}
public function files(){
return $this->hasManyThrough(Files::class, Customers::class);
}
}

Laravel Eloquent Relationship by Models

I have a table called "categories" and there is a 3 table needed the id of categories
the 3 tables needed a categories id is "activies", "stories", "news" i really want to know how to fix this issue the stories and news table is running well im using php tinker to see if the relationship is running and the table activities it give me a null value, inside the activities table i`d input same category_id in the table of activities it gives always a null value given in php tinker
Models
Category.php
public function stories()
{
return $this->hasMany(Story::class);
}
public function news()
{
return $this->hasMany(Newy::class);
}
public function activites()
{
return $this->hasMany(Activity::class);
}
Activity.php
public function category()
{
return $this->belongsToMany(Category::class, 'category_id');
}
If you are using hasMany in your Category Model, the opposite relationship in your Activity model should be belongsTo() and not belongsToMany()
public function category()
{
return $this->belongsTo(Category::class);
}
You can read more in the documentation here

Same Foreing Key Twice That Refers to One Model

I need to make an advice platform for products. User can make advices under product section. So product has many advices. Also advices belongsTo product. But on product_advices table i have product_id and product_advice_id these both refers to id on products table.
So here is the problem. I can take advices from product_advices table which refers to product_id. But how can i take the other one as product.
product->advices to show advice and user message
each advices as advice and advice->product->name to show adviced product name
I couldn't make a relationship with eloquent between them.
//Product Model
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
//ProductAdvice Model
protected $table = 'product_advices';
public function product() {
return $this->belongsTo('App\Product', 'product_id');
}
//Product Advice Table
Schema::create('product_advices', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->text('body')->nullable();
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('product_advice_id')->unsigned();
$table->foreign('product_advice_id')->references('id')->on('products')->onDelete('cascade');
$table->timestamps();
});
For example :
Iphone has many advices by users.
Iphone->advices brings the advices from product_advices that Iphone belongs to product_id column.
When User recommends Samsung Galaxy S10 to Iphone. Samsung refers to product_advice_id column on product_advices table. But how to show Samsung Galaxy S10 as a product.
$advice->product->name returns Iphone instead of Samsung Galaxy S10.
EDIT 2
After your answer, I understood what you want.
Just update your ProductAdvice model to have another relationship, like:
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
public function related()
{
return $this->belonsTo('App\Product', 'product_advice_id');
}
}
Then you can do:
$product = Apple
$product->advices->message = 'blablabla'
$product->advices->related = 'ProductX'
If you need to to the inverse order, $advice->product->related add the same relationship on your Product model.
EDIT 1
Sorry, you edited the post after my answer...
Could you explain the 'product_advice_id' necessity? You already have the product_id.
Unless you want a pivot table, like: Product -> ProductAdvice -> Advice, which is not needed in your case, since you can just put the advice information in the Advice table and link it to the product (belongsTo), removing the necessity of the ProductAdvice pivot.
I think your structure should be like this:
Product model with all the product data
ProductAdvice model with product_id and the advice information (message, rating, etc)
Then, your Product hasMany Advices and your Advice belongsTo Product:
class Product extends Model
{
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
}
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
}
Finally, you can query the advices of a specific Product to get the advice information:
$product->advices->message
$product->advices->rating
Or query the product name if you have an advice:
$advice->product->name
So, If understand well, you want and advice from product A to return the name of product B.
You can make this by creating multiple method related to the same model.
Your models will look similar at this:
class Product extends Model
{
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
public function relations()
{
return $this->hasMany('App\ProductAdvice', 'related_id');
}
}
class ProductAdvice extends Model
{
public function product()
{
return $this->belonsTo('App\Product', 'product_id');
}
public function relation()
{
return $this->belonsTo('App\Product', 'related_id');
}
}
Then you will have to create another column in your talbe name :related_id (it could be something else, just made it match in your model. You can also change relations() and relation() method to whatever name you want.)
After this it's how you store your data. You have to make your code associate the good product model into your product_id and into related_id. This way. You can have $advice->product->name === 'iPhone' && $advice->relation->name === 'Samsung S10'
Found a solution like this for now.
//ProductAdvice Model
protected $table = 'product_advices';
public function user()
{
return $this->belongsTo('App\User');
}
public function product() {
return $this->belongsTo('App\Product', 'product_id');
}
public function advicedProduct()
{
return Product::where('id', $this->product_advice_id)->first();
}
//Product Model
public function advices()
{
return $this->hasMany('App\ProductAdvice', 'product_id');
}
How i show it on view
#foreach($product->advices as $advice)
<li>{{ $advice->body }} - {{ $advice->advicedProduct()->name }}</li>
#endforeach

Laravel Polymorphic Many to Many relationship

I am very new to polymorphic relationships and am struggling on my first task. My real life scenario is complicated, so for the purpose of this question, I have simplified it a little.
I have a range of products. Each of these products can be 'tagged' to one or more "Categories", "Brand" and "Consumer". For example:
I figured with this setup, I would need a table for my polymorphic relationships as below:
I have created a new Taggable class which contains the following
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Taggable extends Model
{
public function taggable()
{
return $this->morphTo();
}
}
...and added a method to my Product class:
public function taggedCategories()
{
return $this->morphMany(Taggable::class, 'taggable');
}
Finally, in my ProductController, I am trying to retrieve all products with their relationships as such:
$products = Product::with('taggedCategories')
Whilst this isn't producing an error, there are no categories returned in my results. Looking at the SQL output in LaravelDebugBar, I see the following SQL.
select * from `taggables` where `taggables`.`taggable_id` in (1) and `taggables`.`taggable_type` = 'App\Models\Product'
This clearly is not right, but I cannot for love nor money figure out where I have gone wrong. I feel I am close, but not quite there.
Can anyone explain what is wrong? Also, would I need to do something different for getting the "Brand" as this is a one-to-many relationship, not many-to-many?
Thanks
Your model structure is going to be like:
class Categories extends Model
{
public function products()
{
return $this->morphToMany('App\Tag', 'productable');
}
}
class Brand extends Model
{
public function products()
{
return $this->morphToMany('App\Tag', 'productable');
}
}
// and Consumers, ....
User Model:
class Product extends Model
{
public function categories()
{
return $this->morphedByMany('App\Categories', 'productable');
}
public function brands()
{
return $this->morphedByMany('App\Brunds', 'productable');
}
}
Database schema:
categories
id - integer
...
brands
id - integer
...
consumer
id - integer
...
productable
product_id - integer
productable_id - integer
productable_type - string
Now, you can retrieve the relations:
$categories = App\Categories::find(1);
// retrieve product of a type
foreach ($categories->products as $product) {
//
}
$product = App\Product::find(1);
// retrieve categories of a product
foreach ($product->categories as $categories) {
//
}
Actually, your type product (categories, brands, consumers) are productable.

hasMany relation not working in laravel

My cart model has:
public function product() {
return $this->hasMany('App\Product');
}
My Product Model has:
public function cart() {
return $this->belongsTo('App\Cart');
}
My database looks like:
Cart Table:
Product Table:
My cart migration contains:
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
My controller contains:
public function index()
{
if(Auth::check())
{
$user = Auth::user();
$cart = Cart::where('user_id',$user->id)->get();
return view('products.cart')->withCart($cart);
}
else {
return view('products.cart');
}
}
It checks if user is logged in then it get details of all cart where user id matches with current user. This is working.
But when I go to product.cart page I try:
#foreach($cart->product as $p)
{{dd($p->name)}}
#endforeach
I get error:
Undefined property: Illuminate\Database\Eloquent\Collection::$product (View: D:\wamp64\www\ecommerce\resources\views\products\cart.blade.php)
Can you tell me how to get value from products table with this relationship.
Thanks!
Edit:
After trying different method I got:
Cart Model:
public function product() {
return $this->hasMany('App\Product','id','product_id');
}
Cart Controller:
public function index()
{
if(Auth::check())
{
$user = Auth::user();
$cart = Cart::where('user_id',$user->id)->first();
return view('products.cart')->withCart($cart);
}
else {
return view('products.cart');
}
}
Now I am able to get the first product that matches with id.
Is there a way to get all product that matches
You use $...->get() returns collection. Use first() instead.
$cart = Cart::where('user_id',$user->id)->first();
if($cart) {
return view('products.cart')->withCart($cart);
} else {
abort(404);
}
Go to User's model:
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Cart');
}
Now, in your controller you'll fetch the logged user's cart. And pass it to the view. e.g
return view('cart.show', ['cart' => Auth::user()->cart]);
Then your foreach will work like expected.
PS: You may have to specify ids on the hasManyThrough relation if you're not using the convention naming for database tables. i.e Model singular (Product) to pluralized tables (products).
Try out the following code.
In your Cart Model
public function product(){
return $this->belongsTo('App\Product','id','product_id')
}
In your Controller
$cart = Cart::where('user_id',$user->id)->first();
In your Cart model you need to specify relation for products:
public function products(){
//return $this->hasMany('App\Product'); should be changed to
return return $this->belongsToMany('App\Product','carts_products', 'cart_id', 'product_id'); // see link to tutorial below.
}
With this relation you can enable lazy loading of products when you get Cart object for user to achieve this in your controller you need:
public function index()
{
if(Auth::check())
{
$user = Auth::user();
$cart = Cart::with('products')->where('user_id',$user->id)->first();
//we get cart via first() as we need single instance of cart not an array of carts,
//even there's only one cart per user get() will return an array of one item
// with('products') lazyloads linked products to cart as you get cart instance
return view('products.cart')->with('cart',$cart);
}
else {
return view('products.cart');
}
}
Later in your view you'll be able to iterate over lazy loaded products array like:
#foreach($cart->products as $p)
{{dd($p->name)}}
#endforeach
Think of creating relations between models in terms of how you'll access them in future, most of the time you don't need to overspecify all relations between objects, as only some of them you'll realy use in code.
Hope it'll help you.
EDIT
change relation type to belongsToMany and it should work, as your Cart has many Products. You still need to get one single Cart instance with first() method byt related Products will be loaded as you need.
Important:
Look at this tutorial https://scotch.io/tutorials/a-guide-to-using-eloquent-orm-in-laravel as you need to change your database schema to achieve this. Currently your schema allows you to have only one product per cart. Look into this example where they create picnics to bears table bears_picnics - in your case you'll have products to carts table carts_products walk through tutorial in you case cart is bear and picnics are products.
Example carts_products schema:
Schema::create('carts_products', function(Blueprint $table)
{
$table->increments('id');
$table->integer('cart_id'); // the id of cart
$table->integer('product_id'); // the id of product in cart
$table->timestamps();
});
remove product_id field from you cart schema as it's useless in such case. if you need cascade deletion specify it on carts_products table.
Happy codding! Thank you for a good question:) making us to make a research deeper.

Resources