I have two tables one is templates and the other is template_images
Showing migration of the two tables.
Templates
Schema::create('templates', function (Blueprint $table) {
$table->increments('id');
$table->string('title', 155);
.....
});
Template Images
Schema::create('template_images', function (Blueprint $table) {
$table->increments('id');
$table->integer('template_id')->unsigned();
$table->boolean('master');
$table->binary('image');
$table->foreign('template_id')->references('id')->on('templates')->onDelete('cascade');
...
});
I have made relations in the both Models like this
Template-Model
class Template extends Model
{
public function tempImage()
{
return $this->hasMany('App\Models\TemplateImage', 'template_id');
}
}
Template Image-Model
class TemplateImage extends Model
{
public function tempImage ()
{
return $this->belongsTo('App\Models\Template', 'template_id');
}
}
What I want is
Select all from templates and select from template_images where template_images.template_id = templates.id and template_images.master = 1
Update
This is how I need to get the image in blade
$temp->tempImage->image
What I try in my controller
$temps = Template::query()->with('tempImage')->get();
but still cant get it the right way.
<?php
class Template extends Model
{
public function tempImages()
{
return $this->hasMany('App\Models\TemplateImage', 'template_id');
}
public function tempMasterImage(){
return $this->hasOne('App\Models\TemplateImage', 'template_id')->where('master', 1);
}
}
If every template can only have 1 master template image then it's better to create a separate relation in model for that. See the example model Class above. tempMasterImgage() is the new relation and it has hasOne relation with TemplateImage. With this in place you can simply do.
$template->tempMasterImage->image
from anywhere, You just need access to Template object.
Edit
It's better to name your hasMany relationship function as plural. That's why I used tempImages in my example
Example Usage.
$template = Template::with(['tempImages', 'tempMasterImage'])->first();
//if you want tempImages to exclude masterImage as we have now seperate relation for that then
$template = Template::with(['tempImages' => function($query){
$query->where('master', '<>', 1);
}])->with('tempMasterImage')->first();
//get master image
$template->tempMasterImage->image
//get all images
foreach($template->tempImages as $tempImage){
echo $tempImage->image //
}
If your $temp variable is an instance of model Template, you can not do $temp->tempImage->image becouse tempImage is a hasMany relationship.
So you can do two things, first:
$temps = Template::with(['tempImage' => function($query){
$query->where('master',1);
}])->get();
And then in your view
foreach ($temps as $temp)
{
foreach ($temp->tempImage as $temp_image)
{
// Here your code for $temp_image->image
}
}
Or second
$temps = TemplateImage::where('master',1)->with('tempImage')->get();
And then in your view
foreach ($temps as $temp)
{
// Here your code for $temp->image
}
Related
Hello Actually am trying to create an app in which every project has some users i.e ProjectMembers.
here is project model with TeamMembers function.
class Project extends Model
{
use HasFactory;
function TeamMembers(){
return $this->hasMany(ProjectMember::class);
}
}
project members table schema.
Schema::create('projects_members', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('project_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->foreign('project_id')->references('id')->on('projects');
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
Now for updating project members i have to first delete relationships from Project members and then saving new one. because i have added multiselect dropdown. $request->team_members have type array.
public function update(Request $request, $id)
{
// return $request;
$project = Project::findorfail($id);
$project->name = $request->project_name;
$project->details = $request->details;
$project->start_date = $request->start_date;
$project->end_date = $request->end_date;
$members = $request->team_members;
ProjectMember::where('project_id', $id)->delete();
$this->update_project_memebers($members, $project);
return redirect('/projects');
}
public function update_project_memebers($members, $project){
foreach ($members as $member_id) {
$project_member = new ProjectMember();
$project_member->project_id = $project->id;
$project_member->user_id = $member_id;
$project_member->save();
}
}
here am deleting cuz if someone created project with two members and when the he/she want to update then he/she can remove one member from multiselect then i have to delete relationship cuz he/she selected only one user.
I don't think it's a good practice, so can i achieve this same func. with another way?
thankyou.
First of all please make a proper naming convention of pivot tabel according to laravel and same for the model name too.
As of now the solution is
define relation as
Project
public function teamMembers()
{
return $this->belongsToMany(ProjectMember::class, 'projects_members', 'project_id', 'member_id');
}
ProjectMember
public function projects()
{
return $this->belongsToMany(Project::class, 'projects_members', 'member_id', 'project_id');
}
So now instead of deleting use sync() method. AS
$project->teamMembers()->sync($members);
Important docs,
https://laravel.com/docs/8.x/eloquent-relationships#syncing-associations
I have a model named Point having the following fields:
name
description
lat
lng
The fields "name" and "description" can be in several languages, so I created two tables for points and their details.
Schema::create('points', function (Blueprint $table) {
$table->id();
$table->float('lat');
$table->float('lng');
$table->timestamps();
Schema::create('point_details', function (Blueprint $table) {
$table->id();
$table->integer('point_id');
$table->string('name');
$table->string('description');
$table->string('lang');
There is an index unique on point_id/language.
In the model files I have One To Many relationships
class Point extends Model
{
public function details()
{
return $this->hasMany(PointDetail::class);
}
}
class PointDetail extends Model
{
public function point()
{
return $this->belongsTo(Point::class);
}
}
Now I want to get the Point with details based on User language. I do so in the PointController:
class PointController extends Controller
{
public function show($id)
{
$point = Point::with(['details' => function($query) {
$query->where(
'lang', Auth::user()->lang ?
Auth::user()->lang :
'it');
}])->find($id);
return view('points.show',compact(['point']));
}
}
Can I avoid the "with" clause in the Controller? Maybe making the right query in the Point model file. I'm looking for a way to return the point with one detail associated with it, based on language of the Auth::user().
Thanks for any suggestion.
you can add query to relationship method in point class to details method
like that :
class Point extends Model
{
public function details()
{
return $this->hasMany(PointDetail::class)->where('lang', Auth::user()->lang);
}
}
UserProductsController
class UserProductsController extends Controller
{
public function index()
{
$products = Product::get();
return view ('products')->with(compact('products'));
}
public function product_categories()
{
$categories = Category::all();
return view ('categories')->with(compact('categories'));
}
}
products_table
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('prod_name');
$table->string('prod_brand')->nullable();
$table->unsignedBigInteger('cat_id');
$table->string('prod_image_path')->nullable();
$table->timestamps();
$table->foreign('cat_id')
->references('id')
->on('categories')
->onDelete('cascade');
});
}
categories_table
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('cat_name');
$table->string('cat_image_path')->nullable();
$table->string('cat_description')->nullable();
$table->timestamps();
});
}
Product Model
class Product extends Model
{
public function category()
{
return $this->belongsTo('App\Category','category_id');
}
}
Category Model
class Category extends Model
{
public function category()
{
return $this->hasMany('App\Product');
}
}
My route is
Route::get('/All_Products', 'UserProductsController#index')->name('All_Products');
Route::get('/product_categories', 'UserProductsController#product_categories')->name('categories');
How can I get all products with the same category? As this is my first project I am spending much more time on this. But nothing worked for me. Can anyone guide me, please?
Assuming you setup your relationships correctly (which they are not)
You have a few ways with Eloquent:
$products = Category::findOrFail($categoryId)->products;
$products = Product::where('category_id', $categoryId)->get();
$products = Product::whereHas('category', function ($query) use ($categoryId) {
$q->where('id', $categoryId);
})->get();
To list a few.
Laravel 7.x Docs - Eloquent - Retrieving Single Models / Aggregates findOrFail
Laravel 7.x Docs - Eloquent - Relationships - Relationship Methods vs Dynamic Properties
Laravel 7.x Docs - Eloquent - Relationships - Querying the Existence of Relationships whereHas
First of all, you have not defined your relationships correctly. It should be something like this:
class Product extends Model
{
public function category()
{
return $this->belongsTo('App\Category');
}
}
class Category extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
Then in your products migration file, cat_id should be renamed to category_id. That way, you don't need to specify the foreign key on the relationship.
I assume you want to list all the products that belong to a particular category. You can easily do that with the route model binding. In which case your route should look something like:
Route::get('categories/{category:id}/products', [CategoryController::class, 'products']);
And then in your controller:
use App\Category;
class CategoryController extends Controller
{
public function products(Category $category)
{
$category->load('products');
return view('products')->withCategory($category);
}
}
You can access the list of products in your blade view like so: $category->products
You need to make an adjustment to your Category model as a Category has many Products. As it stands, the naming of the relationship doesn't reflect that
class Category extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
You can then access the products via the Category model like so.
$categories = Category::with('products')->all();
$categories->each(function($category) {
$products = $category->products;
// Dump & Die a collection of products
dd($products);
});
Note: I have eager loaded the relationship using the with() method, this is just to prevent n+1 queries. More information on eager and lazy loading can be found on the documentation.
You can do something like,
$products = product::with('categories')->get();
foreach($products as $product)
{
foreach($product->categories as $category)
{
echo $category->name;
}
}
$categories = Category::with('products')->get();
$category = Category::with('products')->find($category_id);
I am working on a project in which there are events, which each relate to two single forms on two separate relations – booking and survey. These forms are identically constructed, making it seem unnecessary to use two entirely distinct form models – I instead wanted to use a polymorphic relation, but it appears that isn't possible.
What is the appropriate way to structure this relationship?
Events have one or no booking form
Events have one or no survey form
Forms are a separate, single table
What I have tried:
Polymorphic relationship: Not compatible with two relations to the same model.
Has one relationship: This used a booking_id and survey_id but refused to set either of these fields.
Has many relationship with a type field: Made it difficult to easily save the forms, as it wasn't possible to save to the single relationship. There was also no restriction on the number of forms.
class Event extends Model
{
public function booking()
{
return $this->hasOne(Form::class, 'id', 'booking_form_id');
}
public function survey()
{
return $this->hasOne(Form::class, 'id', 'survey_form_id');
}
}
...
class Form extends Model
{
public function event()
{
return $this->belongsTo(Event::class);
}
}
...
$event = new Event;
$event->name = 'Event';
$event->save();
$booking = new Form;
$booking->name = 'booking';
$event->booking()->save($booking);
$survey = new Form;
$survey->name = 'survey';
$event->survey()->save($survey);
...
Schema::create('events', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->unsignedInteger('booking_form_id')->nullable()->index();
$table->unsignedInteger('survey_form_id')->nullable()->index();
$table->timestamps();
});
Schema::create('forms', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
What would be preferable:
Using a polymorphic relationship which would allow forms to be used in other parts of the application.
Using multiple hasOne relationships to limit the number of forms to one for each type.
I think you got your param order wrong. It's hasOne($related, $foreignKey, $localKey)
class Event extends Model
{
/* if you haven't changed the default primary keys, $localKey should be equal to 'id' */
public function booking()
{
return $this->belongsTo(Form::class, 'booking_form_id');
}
public function survey()
{
return $this->belongsTo(Form::class, 'survey_form_id');
}
}
class Form extends Model
{
public function booking_event()
{
return $this->hasOne(Event::class, 'booking_form_id');
}
public function survey_event()
{
return $this->hasOne(Event::class, 'survey_form_id');
}
}
Now there's 2 ways you can go about this.
If a Form can belong to both kind of events, you need to return a collection when accessing $form->event.
If a Form can belong to only one kind of event, you need to guess which kind and return the model when accessing $form->event.
# Form model
# 1. can be achieved using an accessor. Cannot be eager loaded but can be appended with the $appends Model property
public function getEventsAttribute()
{
return collect([$this->booking_event, $this->survey_event]);
}
# Form model
# 2. can be achieved using a relationship that guesses which relation it should return. Since it returns a relationship, it can be eager loaded.
public function event()
{
return ($this->booking_event()->count() != 0) ? $this->booking_event() : $this->survey_event();
}
i have category_content, contents and users tables which each contents belong to many category_content and category_contentbelong to many contents and each contents belongs to one user and one user has many post on contents table
class Contents extends Model
{
...
public function categories()
{
return $this->belongsToMany(ContentCategories::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class ContentCategories extends Model
{
...
public function contents()
{
return $this->belongsToMany(Contents::class);
}
}
class User extends Authenticatable
{
...
public function content()
{
return $this->hasMany(Contents::class);
}
}
by this below code i can find categories content that category id is 7
$nodejsContents = ContentCategories::find('7')->contents;
now, problem is here, how can i get post owner in this query which content is belongs to which user
i tested this code but i get error:
$nodejsContents = ContentCategories::find('7')->contents->user;
Error:
"Undefined property: Illuminate\Database\Eloquent\Builder::$contents"
contents_categories migration:
public function up()
{
Schema::create('contents_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('lang',2)->default('fa');
$table->integer('parent')->default(0);
$table->timestamps();
});
}
user is within contents, so access with closure,
$nodejsContents = ContentCategories::has('contents')->with(['contents' => function($query){
$query->with('user')->get();
}])->find('7');
ContentCategories::find('7')->contents
belongsToMany Contents would return a collection. So you could either loop through the collection to get the related user of Contents
or
ContentCategories::find(7)->contents->with('user');