Hello i m stuck with Laravel eloquent relationship - php

i have two tables deals and products, on product table i just have product description whic can be assigned to one than more deals , now i want to display product description on deal page,but my relation is not working correctly,
product table
deal table
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
//
protected $guarded=[];
public function deals()
{
return $this->belongsTo(Deal::class,'product_slug','product_id');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Deal extends Model
{
//
protected $guarded=[];
public function products()
{
return $this->hasMany(Product::class,'product_slug','product_id');
}
}
this is how i m calling the function in controller
public function index()
{
$title='Today Deal';
$deals=Deal::with('products')->get();
// dd($deals);
// return;
return view('welcome',compact('title','deals'));
}
i can see the product data when i do dd but on view i m getting this error when i access the product data
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$title (View: D:\xampp\htdocs\bargin\resources\views\welcome.blade.php)
this how i acces the the product data inside the deal loop
<p>{{$deal->products()->title}}</p>

The problem is inside your loop. <p>{{$deal->products()->title}}</p>.
When you access a relationship using the function call, products(), you are actually returning an instance of the relationship to where you can add query methods to.
For example, you can use $deal->products()->where(...)->get().
In order to access the actual products, you just use $deal->products.
#foreach($deal->products as $product)
{{ $product->title }}
#endforeach

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

Laravel Multi BelongsTo RelationShip Merge with Eager Loading

Laravel version:7.0
reviews table (Model - Review) has id, product_type, product_id, rating columns.
product_type can be service, plugin, module and each value has own model App\Service, App\Plugin, App\Module. I could put model names directly in product_type but I prefer to use those values.
Here is Review model relationship.
public function plugin()
{
return $this->belongsTo(Plugin::class, "product_id")->withDefault();
}
public function module()
{
return $this->belongsTo(Module::class, "product_id")->withDefault();
}
public function service()
{
return $this->belongsTo(Service::class, "product_id")->withDefault();
}
public function getItem()
{
if($this->product_type=='module')
{
return $this->module;
}elseif($this->product_type=='service')
{
return $this->service;
}else {
return $this->plugin;
}
}
Now I want to get them with eager loading in Review Model as following:
$reviews = Review::with("getItem")->get();
Without Eager loading, I could use $review->getItem()->name // this returns name of product.
How can I get them with eager loading?
You could have implemented this easily as a polymorphic relationship. In your Reviews Model, you could do this:
Model Structure
App\Review.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model
{
public function reviewable()
{
return $this->morphTo();
}
}
Then add reviews() method to your App\Service, App\Plugin and App\Module models
public function reviews()
{
return $this->morphMany('App\Review', 'reviewable');
}
Table Structure
You reviews table could look like this:
reviews
id - integer
body - text
reviewable_id - integer
reviewable_type - string
Note the reviewable_id and reviewable_type fields. The reviewable_id stores the id of the item reviewed and the reviewable_type stores the model related to the item.
Retrieving The Relationship
You may access the relationships via your models. For example, to access all of the reviews for a service, we can use the reviews dynamic property:
$service = App\Service::find(1);
foreach ($service->reviews as $review) {
//
}
You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to morphTo. In your case, that is the reviewable method on the Review model. So, we will access that method as a dynamic property:
$review = App\Review::find(1);
$reviewable = $review->reviewable;
The reviewable will return the model on the Review model either Service, Plugin or Module

Display name of table instead of its id Laravel

I have 2 tables, tn_project has id name cv_id_project and tn_activity has id name id, i want to get the name of the project which is cv_project_name rather its id.
Basically when i click add activity i have select option to choose which project that i use, i manage to get the id of selected project but i want its name(i can select up to 3 project in a single activity)
Model
<?php namespace Activity;
use Illuminate\Database\Eloquent\Model;
class Activity extends Model {
protected $table = 'tn_activity';
public $timestamps = false;
public function projectModel(){
return $this->hasMany('Activity\Project','cv_id_project','id');
}
}
VIEW
#foreach($query as $result)
<td>{{$result->projectModel->cv_project_name}}</td>
#endforeach
Controller
public function index(){
return view('landing-page')
->with('title','Landing Page')
->with('query',Activity::with('projectModel')->get())
->with('projects',Project::all());
}
usually it's work but i don't know what makes it error
and then this error occured
Undefined property: Illuminate\Database\Eloquent\Collection::$cv_project_name (View: C:\xampp\htdocs\PKL\netxcel-2-backup2\resources\views\landing-page.blade.php)
Your Activity projectModel is a collection, because it has hasMany relationship. If it's really true, you should get the project name like this.
#foreach ($result->projectModel as $project)
<td>{{$project->cv_project_name}}</td>
#endforeach
However, if it shouldn't have hasMany relationship than you should change the hasMany part

Getting all objects from hasMany relation and paginate

In my Laravel application, I have a category listing page. When the user clicks on a certain subcategory, I'd like to list all the products and use pagination on that result. I'm already listing all the products related to that subcategory, for now, with the help of a subcategory ID:
public function subcategoryListing($slug){
$products = Subcategory::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
There are three classes involved in this structure: Category, Subcategory and Products. They were declared as follows:
Category
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use App\Subcategory;
class Category extends Model
{
protected $table = 'category';
public $timestamps = false;
public function subCategory(){
return $this->hasMany('App\Subcategory', 'category_id');
}
}
Subcategory
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Subcategory extends Model
{
protected $table = 'subcategory';
public $timestamps = false;
public function products(){
return $this->hasMany('App\Products');
}
}
Products
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Products extends Model
{
protected $table = 'products';
}
For each model class I have one table, with this structure:
Category
- id
- category_name
SubCategory
- id
- category_id
- subcategory_name
Products
- id
- subcategory_id
- product_title
- description
- price
What I want is to paginate the results retrieved from the query in my page. Is there any better way to fetch the products associated to the subcategory and paginate them?
In Eloquent (Laravel's ORM) when you call a relation as a property ($subCategory->products), it returns the related object or a collection of objects depending on the relation type (belongs to, has many, ...). Instead, if you call it as a function ($subCategory->products()), you get a QueryBuilder instance.
Refer to http://laravel.com/docs/5.1/eloquent-relationships#querying-relations, under section Relationship Methods Vs. Dynamic Properties for more details on that.
Anyway, using the relationship method, you can call paginate() for your collection. Then, with that in mind, you can change your code slightly to get what you want:
public function subcategoryListing($slug) {
// I'm supposing here that in somewhere before
// run the query, you set the value to $idofSubcat
// variable
$products = Subcategory::find($idofSubcat)->products()->paginate();
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
I think, that create two categories table not so correctly, it will be better to use next:
table categories
id category_name parent_id(nullable)
and Products
id category_id product_title description price
It's more usefull, you can remove one Subcategory model and do all in Category.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'category';
public $timestamps = false;
public function subCategory(){
return $this->belongsToMany('App\Category', 'categories', 'id', 'parent_id');
}
public function products(){
return $this->hasMany('App\Products');
}
}
and products model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Products extends Model
{
protected $table = 'products';
public function category() {
return $this->belongsTo('App\Category')
}
}
then, you can get the query result
public function subcategoryListing($slug){
$products = Category::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->withProducts($products); // it's a magic))
}
But, existing one not pretty thing. Do you really sure, that products will be only in one category?)
I couldn't really understand some of your initial function:
// $slug is not being used anywhere within the function
public function subcategoryListing($slug){
// $idOfSubcat isn't passed to this function so will throw an error
$products = Subcategory::find($idofSubcat)->products;
return view('pages.subcategorylisting')
->with(array(
'products' => $products,
));
}
If all you're trying to do is paginate the Products that belong to a specific subcategory_id, given you have the subcategory_id then the following code will work:
$products = Product::where('subcategory_id', $idofSubcat)->paginate();
Then you can return this paginated collection to your view as you're already doing:
return view('pages.subcategorylisting')
->with(compact('products'));

Is there a way to morph a Laravel Eloquent model?

I have a Detail (represents order details) model that I'd like to morph to either a sales order detail or a purchase order detail. So I create a table that has a 'type' column, which would have a value of 'sale' or 'purchase'.
My question is, is there a way in Laravel that I could morph the Detail model to Sale and Purchase, so that, for example, if I call App\Sale::all() it would fetch App\Detail::all()->where('type','sale') ?
Set the database tables:
You can set up your database tables in this structure :
purchases
id - integer
price - string
sales
id - integer
price - integer
details
id - integer
description - string
detailable_id - integer
detailable_type - string
Set your models:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Detail extends Model
{
// Get all of the owning detailable models.
public function detailable()
{
return $this->morphTo();
}
}
class Sale extends Model
{
// Get all of the sales member's order details.
public function details()
{
return $this->morphMany('App\Detail', 'detailable');
}
}
class Purchase extends Model
{
// Get all of the purchase's order details.
public function details()
{
return $this->morphMany('App\Detail', 'detailable');
}
}
Retrieve data :
And then you can retrieve your sales like this :
$sales = App\Sale::find(1);
foreach ($sales->details as $order_detail) {
//
}
Same thing with purchases :
$purchases = App\Purchase::find(1);
foreach ($purchases->details as $order_detail) {
//
}
More about polymorphic relations : http://laravel.com/docs/5.1/eloquent-relationships#polymorphic-relations
Although I haven't found an "official" way to morph a single class to another. I developed the following way that could be a solution.
First, define two models Sale and Purchase that extends Detail, and use the trait that will define later.
class Sale extends Detail {
use SaleTrait;
}
Then, use GlobalScope to add constraints to query builder. Here are the steps:
Define a trait for Sale and Purchase model,
trait SaleTrait {
public static function bootSaleTrait()
{
static::addGlobalScope(new ActiveEventsScope);
}
}
Then define the scope. NOTE: here instead of implementing ScopeInterface, I extends Sofa\GlobalScope which handles remove() method for me, so I only need to define apply() in the scope.
class SaleScope extends GlobalScope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('type', 'sale');
}
}
Now I could use App\Sale::all() and App\Purchase::all() to only retrieve what I want.

Categories