Laravel ManyToMany relation with singular table names - laravel

I want to create ManyToMany relation between samples and supposed_origins.
In my firm, our convention is to have singular table names, so as mentioned in https://laravel.com/docs/8.x/eloquent#table-names, I defined :
migrations
Schema::create('v3_sample', function (Blueprint $table) {
$table->increments('id');
$table->string('number_canonical', 6)->unique(); // business key
$table->string('name')->nullable();
};
Schema::create('v3_supposed_origin', function (Blueprint $table) {
$table->increments('id');
$table->integer('location_id')->unsigned(); // from other database
$table->string('description');
});
Schema::create('v3_sample_supposed_origin', function (Blueprint $table) {
$table->increments('id');
$table->integer('sample_id')->unsigned();
$table->foreign('sample_id')->references('id')->on('v3_sample')->onDelete('cascade');
$table->integer('supposed_origin_id')->unsigned();
$table->foreign('supposed_origin_id')->references('id')->on('v3_supposed_origin')->onDelete('cascade');
});
models
class Sample extends Model
{
protected $table = 'v3_sample';
public function supposed_origins() {
dd($this);
return $this->belongsToMany('App\SupposedOrigin');
}
}
class SupposedOrigin extends Model
{
protected $table = 'v3_supposed_origin';
public function samples() {
return $this->belongsToMany('App\Sample');
}
}
and controller
class SampleController extends Controller
{
// […]
public function edit(Request $request, $number_canonical)
{
$sample_record = DB::table('v3_sample')->where('number_canonical', $number_canonical)->first();
$supposed_origins = $sample_record->supposed_origins;
dd($supposed_origins); // returns ErrorException Undefined property: stdClass::$supposed_origins
and SampleController returns ErrorException Undefined property: stdClass::$supposed_origins
As I have singular table names, the rule that define to have pivot table with singular table names (sample_supposed_origin) to plural table names (samples and supposed_origins instead of my sample an supposed_origin) are not followed !
Questions :
Is singular naming the reason it can't find the relation?
Is there a way to specify it correctly? Add pivot table model?
Additional informations : Laravel Framework 7.28.4, PHP 8.0.3, mariadb Ver 15.1 Distrib 10.1.47-MariaDB

Your issue is that you are using the Query Builder, and not the Eloquent Query Builder.
DB::table('v3_sample')->where('number_canonical', $number_canonical)->first() will return an object representing the results from the database, but it is just a standard PHP Object.
You want:
Sample::query()->where('number_canonical', $number_canonical)->first()
This will return a Sample Model and you will have access to the relation you need.
In your case $sample_record->supposed_origins.

As #KurtFriars mentionned in his answer, I change my controller to :
public function edit(Request $request, $number_canonical)
{
$sample_record = Sample::query()->where('number_canonical', $number_canonical)->first();
$supposed_origins = $sample_record->supposed_origins;
and previous error disappears but I have an other error :
SQLSTATE[42S02]: Base table or view not found: 1146 Table dbname.sample_supposed_origin' doesn't exist`
To have my supposed_origins, It requires also to improve my Sample model like this :
public function supposed_origins() {
return $this->belongsToMany(
'App\SupposedOrigin',
'v3_sample_supposed_origin',
'sample_id',
'supposed_origin_id'
);
}

Related

Summary table for the model

There is a model ObjectType and a model GuestOptions
I want to create a pivot table, I do this
public function up()
{
Schema::create('object_type_options', function (Blueprint $table) {
$table->unsignedBigInteger('object_type_id');
$table->unsignedBigInteger('guest_option_id');
$table->foreign('object_type_id')->references('id')->on('object_types')->onDelete('cascade');
$table->foreign('guest_option_id')->references('id')->on('guest_options')->onDelete('cascade');
$table->primary(['object_type_id','guest_option_id']);
});
}
In the model I define the relation ObjectType , but there is no result
public function options()
{
return $this->belongsToMany(GuestOptions::class,'object_type_options');
}
What am I doing wrong?
Can give two basic and quick solutions.
public function options()
{
return $this->belongsToMany(GuestOptions::class,'object_type_options','object_type_id','guest_option_id');
}
you can also use the relation more clearly like below. but need to change some attributes in the database.
public function options()
{
return $this->belongsToMany(GuestOptions::class);
}
And Table need to change
public function up()
{
//table name changed
Schema::create('guest_options_object_type', function (Blueprint $table) {
$table->unsignedBigInteger('object_type_id');//actual table name
$table->unsignedBigInteger('guest_options_id'); //actual table name
$table->foreign('object_type_id')->references('id')->on('object_types')->onDelete('cascade');
$table->foreign('guest_options_id')->references('id')->on('guest_options')->onDelete('cascade');
$table->primary(['object_type_id','guest_option_id']);
});
}

Laravel Models and displaying them on view together

For a school assignment we have two tables and models players and countries
I know its either my models or my controller (I've had this problem before but in another assignment their was only one country so i just looped through a country variable and used arrays instead but this wont work for multiple countries)
When I try to display in the view i get "Trying to get property of non-object 'name' on the {{$player->country->name}} and this is the way the teacher has explicitly said we are to display it.
Currently before anything else id like to display all my players and their country names
Models
class Country extends Model
{
//
protected $table = 'countries';
protected $fillable=['name','flag'];
public function player(){
return $this->hasMany(Player::class);
}
}
class Player extends Model
{
//
protected $fillable =['name','age','role','batting','bowling','image','odiRuns','countries_id'];
public function country()
{
return $this->belongsTo(Country::class);
}
}
Tables
public function up()
{
Schema::create('countries', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('flag');
$table->timestamps();
});
}
public function up()
{
Schema::create('players', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('age');
$table->string('role');
$table->string('batting');
$table->string('bowling');
$table->string('image');
$table->string('odiRuns');
$table->integer('countries_id')->unsigned();
$table->foreign('countries_id')->references('id')->on('countries');
$table->timestamps();
});
}
Controller
use App\Player;
use App\Country;
use Illuminate\Http\Request;
class PlayerController extends Controller
{
public function index()
{
//
$players=Player::all();
return view('index',compact('players'));
}
View
#extends('layout')
#section('content')
#foreach ($players as $player )
{{$player->name}}
{{$player->age}}
{{$player->role}}
{{$player->batting}}
{{$player->bowling}}
{{$player->odiRuns}}
{{$player->country->name}}
#endforeach
#endsection
Edit
players all have country id's relating to the countries table
Tables
players table
countries table
The problem is your missing foreign key in relation. When you are defining a relationship, if you explicitly don't tell which foreign key to use for the relationship, Laravel looks for a foreign key like relationname_primarykeyoftheparenttable. In your case its country_id but in your players table the column name is countries_id. So relationship is not building and you are getting error. Change the column name or tell the relationship which foreign key to use to build the relationship.
class Player extends Model
{
protected $fillable =['name','age','role','batting','bowling','image','odiRuns','countries_id'];
public function country()
{
return $this->belongsTo(Country::class,'countries_id');
}
}
Laravel Doc

Why does Laravel Combine query is not working?

My Channels Model is :
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Model is:
class Channels extends Model
{
protected $fillable = ['title','slug'];
public function Discussion()
{
return $this->hasMany('App\Discussion');
}
}
Channel Migration File :
class CreateChannelsTable extends Migration
{
public function up()
{
Schema::create('channels', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('channels');
}
}
Discusion Migraion file
class CreateDiscussionsTable extends Migration
{
public function up()
{
Schema::create('discussions', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->string('title');
$table->text('content');
$table->text('slug');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('discussions');
}
}
**Controller is: **
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function channel($slug)
{
$channel= Channels::where('slug',$slug)->first();
return view('channel')->with('discussion',$channel->Discussions);
}
}
And Finnaly the route is
Route::get('channel/{slug}',[
'uses' => 'HomeController#channel',
'as' => 'channel']);
**Now i am trying to fetch the data: **
$channel->Discussions
but it's giving me the error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'discussions.channels_id' in 'where clause' (SQL: select * from discussions where discussions.channels_id = 1 and discussions.channels_id is not null)
I don't know what exactly to do now. Please Help me
First of all, it's very important to name your classes and its relationships appropriately.
Laravel makes some assumptions about your foreign keys based on your class names and relationship definitions. You could also specify the foreign keys if you want.
However, here's how i'd do it.
class Channel extends Model
{
protected $fillable = ['title','slug'];
public function discussions()
{
return $this->hasMany('App\Discussion');
}
}
Notice here the model name is called Channel. When we have this class name and relationship, Laravel will assume that the discussion model has a foreign key called channel_id which it does.
You also need to define an inverse relationship in your Discussion model
class Discussion extends Model
{
public function channel()
{
return $this->belongsTo('App\Channel');
}
}
Now, doing the following would work:
public function channel($slug)
{
$channel = Channel::whereSlug($slug)->first();
return view('channel')->with('discussion', $channel->discussions);
}
If you are tied and cannot change the model name for whatever reason, then you need to specify a foreign key on your relationship definitions.
For example:
public function discussions()
{
return $this->hasMany('App\Discussion', 'channel_id');
}
public function channel()
{
// This one can stay the same as Laravel will try to match `channel_id`
// on the discussion table to the id on the channels table
return $this->belongsTo('App\Channel');
}
Fore more information, read about Eloquent Relationships.
Sorry, I was already writing this up when #Julien Metral commented, but this is an extension of what he already said :)

How to retrieve Nested Relationship data in laravel 5 using eloquent?

Having some problems retrieving nested relationship data. Here are my models:
class Partner extends Model
{
public function admins()
{
return $this->hasMany(Resource::class)->where('resource_type', 'Admin');
}
}
class Resource extends Model
{
public function details() {
return $this->belongsTo(ResourceDetail::class);
}
}
class ResourceDetail extends Model
{
}
When I try $this->partner->admins[0]->details it's giving null. The sql it generated is: "select * from resource_details where resource_details.id is null". I'm not quite sure why is it null in the query. I must have done something wrong with the relations. I tried $this->partner->with('admins.details')->find($this->partner->id)->toArray();. It's getting the admins, but details is still null. I also tried hasManyThrough, like: return $this->hasManyThrough(ResourceDetail::class, Resource::class)->where('resource_type', 'Admin'); it finds "unknown column". This is my database structure:
Schema::create('partners', function (Blueprint $table) {
$table->increments('id');
});
Schema::create('resources', function (Blueprint $table) {
$table->increments('id');
$table->integer('partner_id')->nullable()->unsigned();
$table->foreign('partner_id')->references('id')->on('partners')
->onUpdate('cascade')->onDelete('set null');
$table->enum('resource_type', constants('RESOURCE_TYPES'))->nullable();
$table->integer('resource_detail_id')->unsigned();
$table->foreign('resource_detail_id')->references('id')->on('resource_details')
->onUpdate('cascade')->onDelete('cascade');
});
Schema::create('resource_details', function (Blueprint $table) {
$table->increments('id');
});
Do I need to change the structure? Or, how can I get the data from current structure? All I want is, a partner has many resources, and a resource has one details.
From that error I think you may be trying to call $this->partner->admins[0]->details from a model that doesn't have an id. What is $this in context to?

One to many relationship returns NULL

I have the following Migrations:
Table: bebidas:
class CreateBebidasTable extends Migration{
public function up() {
Schema::create('bebidas', function ($table) {
$table->increments('id');
$table->integer('tipo_id')->unsigned();
$table->string('bebi_name');
$table->string('bebi_size');
$table->float('bebi_price');
$table->timestamps();
$table->foreign('tipo_id')->references('id')->on('tipobebidas');
});
}
public function down() {
Schema::drop('bebidas');
}
}
Table: tipobebidas
class CreateTiposBebidasTable extends Migration {
public function up()
{
Schema::create('tipobebidas', function($table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::drop('tipobebidas');
}
}
These are the models:
class Bebida extends Eloquent{
public function TipoBebida() {
return $this->belongsTo('TipoBebida');
}
}
class TipoBebida extends Eloquent{
protected $table = "tipobebidas";
public function Bebidas() {
return $this->hasMany('Bebida');
}
}
Each Bebida (drink) has a TipoBebida (drink type) and vive-versa. I'm trying to get a composed table showing all fields from the bebidas table and tipobebidas table.
Based on Laravel's documentation on eager loading, I'm running the following command:
$bebidas = Bebida::with('tipobebida')->get();
At this point $bebidas has the following value: (I'm removing the timestamps fields)
[
{"id":1,"bebi_name":"COCA-COLA","bebi_size":"1 litro",
"bebi_price":4,"tipo_id":1,"tipobebida":null},
{"id":2,"bebi_name":"COCA ZERO","bebi_size":"1 litro",
"bebi_price":4,"tipo_id":1,"tipobebida":null}
]
Instead of "tipobebida":null, I was expecting something like "name":"refrigerantes" or some representation of the tipobebidas table contents.
I inspected the SQL commands being run, here it is:
select * from `tipobebidas` where `tipobebidas`.`id` in (?)
How can I get this to work?
I'll be using this data on a couple nested foreach loops to show the drinks Bebida grouped by type TipoBebida.
Thank you!
I got it to work. It all came down to naming conventions.
Here's what I did:
-The name of the foreign id field must be the singular of the table name plus _id, therefore the migration for bebidas was changed to the following:
class CreateBebidasTable extends Migration{
public function up() {
Schema::create('bebidas', function ($table) {
$table->increments('id');
$table->integer('tipobebida_id')->unsigned(); // ** new field name **
$table->string('name');
$table->string('size');
$table->float('price');
$table->timestamps();
});
}
public function down() {
Schema::drop('bebidas');
}
}
Also, the foreign key relationship was producing a SQL error, tried to fix it, still nothing, so I removed the following line: $table->foreign('tipo_id')->references('id')->on('tipobebidas');
Everything else remained unaltered.
The eager loading is working.
Thanks everyone for your help!!!
First, in tables bebidas and tipobebidas I don't see and foreign keys...
I think in bebidas you should have tipobebidas_id and it is a foreign key to tipobebidas id field.
After you do that, change your model methods to:
class Bebida extends Eloquent{
protected $table = "bebidas";
public function TipoBebida() {
return $this->belongsTo('TipoBebida', 'tipobebida_id');
}
}
class TipoBebida extends Eloquent{
protected $table = "tipobebidas";
public function Bebidas() {
return $this->hasMany('Bebida');
}

Resources