How to get foreign keys from laravel model? - php

is there any way to get the foreign keys from a laravel model?
I've found that you can get the primary key by using the method getKeyName(); but havn't found any solution to get the foreign keys.
For example class Grades has the following relations:
public function student()
{
return $this->belongsTo(Student::class, 'student_id', 'id');
}
public function subject()
{
return $this->belongsTo(Subject::class, 'subject_id', 'id');
}
and the following migration:
Schema::create('grades', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('student_id');
$table->unsignedBigInteger('subject_id');
$table->float('grade', 3, 1);
$table->dateTime('graded_at');
$table->timestamps();
$table->foreign('student_id')
->references('id')
->on('students');
$table->foreign('subject_id')
->references('id')
->on('subjects');
$table->unique(['student_id', 'subject_id']);
});
The desired result would be something like:
$grades= new Grades();
return $grades->getForeignKeys();
// desired result
// [
// student_id,
// subject_id
// ]
Is there any way to get all the foreign keys without being able to alter the model?

Eloquent is a pretty magic ORM, it needs nearly no configuration to sync your database to your models but this freedom is at a cost: it doesn't know your database modelisation.
There is no automatic way of getting foreign keys name from a model. But you can make it work by yourself:
Defining relationship like you did returns a relation object, like BelongsTo. Relationship objects have methods to get foreign keys that you defined like getForeignKeyName
So you can do something like this:
$grade = Grade::findOrFail($id);
$fks = [];
$fks [] = $grade->student()->getForeignKeyName();
$fks [] = $grade->subject()->getForeignKeyName();
Another approach would be to add an attribute in your models that stores the foreign keys you want to use, use it as a single source of truth for your relationship definition too, like this:
class Grades extends Model{
protected $foreignKeys = [
'subject' => 'subject_id',
'student' => 'student_id'
];
public function student()
{
return $this->belongsTo(Student::class, $this->foreignKeys['student'], 'id');
}
public function subject()
{
return $this->belongsTo(Subject::class, $this->foreignKeys['subject'], 'id');
}
public function getForeignKeys(){
return array_values($this->foreignKeys);
}
}

Related

Laravel Eloquent relationship returning null

I seem to be having issues with my Eloquent relationships. I have a couple of tables to demonstrate
Schema::create('properties', function (Blueprint $table) {
$table->integer('id')->unsigned()->index();
$table->integer('propertyid')->unsigned()->index();
$table->longText('description')->nullable();
$table->boolean('garden')->nullable();
$table->boolean('parking')->nullable();
});
Schema::create('address', function (Blueprint $table) {
$table->integer('propertyid')->unsigned()->index();
$table->string('name')->nullable();
$table->string('street')->nullable();
$table->foreign('propertyid')
->references('propertyid')
->on('properties')
->onDelete('cascade');
});
Removed a lot of columns to cut down on code. The relationships are pretty straight forward, within Property.php I define the relationship to address, using the foreign key name.
public function address()
{
return $this->hasOne(Address::class, 'propertyid');
}
And then in Address.php I do the inverse
public function property()
{
return $this->belongsTo(Property::class, 'propertyid');
}
When I populate my database, I can go into the address table, click on the propertyid, and it takes me to the correct property, so it seems to be set up correctly. However, in my Controller, if I do
$properties = Property::with('address')->paginate(6);
And I output this using dd, the relations output returns null
#relations: array:6 [
"address" => null
]
Why does this not pick up the relationship?
Thanks
You have to mention localkey as well in hasOne relation.Looks like mapping between two table columns are propertyid not id as per migration
$table->foreign('propertyid')
->references('propertyid')
So realtionship should be
public function address()
{
return $this->hasOne(Address::class, 'propertyid','propertyid');
}
Also need yo change belongsTo relationship as well
public function property()
{
return $this->belongsTo(Property::class, 'propertyid', 'propertyid');
}

Laravel Eloquent getting data from relations

I have Task model. My Task model has some relationships and it currently looks like this:
class Task extends Model
{
use HasFactory;
public $timestamps = false;
public function city()
{
return $this->hasOne(City::class, 'id', 'city_id');
}
public function type()
{
return $this->hasOne(Type::class, 'id', 'type_id');
}
public function note()
{
return $this->hasOne(Note::class, 'id', 'note_id');
}
public function operator()
{
return $this->hasOne(User::class, 'id', 'operator_id');
}
}
Now, in my TasksController I need to get Tasks that match certain criteria, like this:
$tasks = Task::whereCityId($city->id)->whereTypeId($type->id)->get()->toArray();
The problem is that fields named city_id type_id note_id operator_id will get my integer values that they have.
Instead I would like to get certain value from a related Model.
For example:
operator_id should be replaced with username from User table that corresponds to the user id.
An obvious solution to this would be to simply use foreach loop, go through my results and get the data I need and simply create another array with the information replaced, but I am not sure if this is the best idea and perhaps there is something better.
You have to change in your code:
$this->hasOne(ClassName::class, 'id', 'foreign_key');
To
$this->belongsTo(ClassName::class, 'foreign_key', 'id');
because Task's id does not available as foreign key in these tables. These table's id present in task table as foreign key so you have to use belongsTo() relationship to tell script from where these id belongs.
Then access properties like this:
$tasks = Task::with("type", "city", "operator")
->whereCityId($city->id)->whereTypeId($type->id)->get();
foreach($tasks as $task){
echo $task->city->name;
}
first you should fix your relation:
public function city()
{
return $this->hasOne(City::class,'city_id','id');
}
and so one the same error, foreign key in argument order comes before the primary key.
after that you can use addSelect:
$tasks = Task::whereCityId($city->id)->whereTypeId($type->id)
->addSelect(['userName' => User::select('name')
->whereColumn('users.id', 'tasks.operator_id')
->limit(1)])->get()->toArray();
i think this will help better than what you ask.
$tasks = Task::whereCityId($city->id)
->whereTypeId($type->id)
->with('operator')
->get()->toArray();
with('operator') is ORM feature that make you collection to include its relation as collection property. In this case it will convert to array property.
you could access it from your foreach function as
#foreach($task as $key)
$key['operator']['username']
#endforeach
Have a nice day

Laravel: Custom Foreign Key in One-To-Many Relationship Does Not Resolve

There are two Models User and Submission and a User can have any number of submissions in which he is referenced as Author. I have set up the relationship according to the documentation and
the result of hasMany() is working fine. Only the belongsTo() method does not return the expected objects.
Here is the migration of my models:
Schema::create('users', function (Blueprint $table) {
$table->id();
});
Schema::create('submissions', function (Blueprint $table) {
$table->id();
$table->foreignId( 'author' );
$table->foreign( 'author' )->references('id')->on('users');
});
And these are the relationships within my models:
class User extends Authenticatable {
public function submissions() {
return $this->hasMany( 'App\Submission', 'author' );
}
}
class Submission extends Model {
public function author() {
return $this->belongsTo( 'App\User', 'author' );
}
}
Now, in my controller I want to execute this code:
$author = Submission::find(1)->author;
dd($author);
But, instead of the user object, the stored integer value of the author ID is printed. The relationship does not seem to be resolved via the author() method.
On the other hand, when I fetch a user and their submissions, everything works as expected, i.e. the submissions are printed as objects:
$u = User::find(1)->submissions;
dd($u);
But the relation from Submission to User does get resolved properly, when I do not define a custom foreign key with belongsTo and rename the colum to author_id.
The configuration that works is this one:
//migration for Submissions table
Schema::create('submissions', function (Blueprint $table) {
$table->id();
$table->foreignId( 'author_id' );
$table->foreign( 'author_id' )->references('id')->on('users');
});
//within Submission model
public function author() {
return $this->belongsTo( 'App\User' );
}
I have the feeling that I'm missing something. Is there any additional link I have to make? I browsed through the documentation and the web for solutions, but to no success. If there is no other solution, I'll follow with renaming the column. But I'd rather understand the cause of my problem and work with my original design.
you should not name relation functions and fields using the same string.
When you have a column author and a public function author() laravel goes for column content first.
Add '_id' to the column, so you do not need to define referenced field in $this->belongsTo( 'App\User', 'author' );.

One to Many Relationship Laravel cannot display the related data

I have two models called DataKelurahan and RegistrasiPasien and have a one-to-many relationship, but I can't access the relationship.
I have made a form for adding patient and save it to the registrasi_pasiens table and it works well. but when I try to display the relation data, it doesn't work properly.
In the registrasi_pasiens table, I have 1 record with kelurahan_id = 3. Then, I try to access it via php artisan tinker with these command:
$kelurahan = App\Domain\DataKelurahan\Models\DataKelurahan::find(3) works fine and data is exist.
$pasien = App\Domain\RegistrasiPasien\Models\RegistrasiPasien::find(2007000001) works fine and the data is exist with kelurahan_id = 3
$kelurahan->pasiens the result is null. Shouldn't it show the pasien data that has kelurahan_id = 3?
$kelurahan->pasiens->nama and the result is like this PHP Notice: Trying to get property 'nama' of non-object in D:/PROFESSIONAL/PROJECT/WEB DEVSeval()'d code on line 1 => null
I don't have any idea what's wrong with my codes. Much appreciate for your help guys.
Below are the models that I have made:
DataKelurahan.php
<?php
namespace App\Domain\DataKelurahan\Models;
use Illuminate\Database\Eloquent\Model;
use App\Domain\RegistrasiPasien\Models\RegistrasiPasien;
class DataKelurahan extends Model
{
protected $fillable = ['nama_kelurahan', 'nama_kecamatan','nama_kota'];
public function pasiens(){
return $this->hasMany('RegistrasiPasien');
}
}
RegistrasiPasien.php
<?php
namespace App\Domain\RegistrasiPasien\Models;
use Illuminate\Database\Eloquent\Model;
use App\Domain\DataKelurahan\Models\DataKelurahan;
class RegistrasiPasien extends Model
{
protected $fillable = [
'nama',
'alamat',
'telepon',
'rt',
'rw',
'tgl_lahir',
'jenis_kelamin'
];
public function kelurahan(){
return $this->belongsTo('DataKelurahan');
}
}
And below are my database tables:
data_kelurahans
Schema::create('data_kelurahans', function (Blueprint $table) {
$table->increments('id');
$table->string('nama_kelurahan');
$table->string('nama_kecamatan');
$table->string('nama_kota');
$table->timestamps();
});
registrasi_pasiens
Schema::create('registrasi_pasiens', function (Blueprint $table) {
$table->increments('id');
$table->integer('kelurahan_id')->unsigned();
$table->string('nama');
$table->string('alamat');
$table->char('telepon', 15);
$table->integer('rt');
$table->integer('rw');
$table->date('tgl_lahir');
$table->string('jenis_kelamin');
$table->timestamps();
});
Schema::table('registrasi_pasiens', function (Blueprint $table){
$table->foreign('kelurahan_id')->references('id')->on('data_kelurahans')->onDelete('cascade');
});
From Docs:
Eloquent will automatically determine the proper foreign key column on
the model. By convention, Eloquent will take the "snake case"
name of the owning model and suffix it with _id.
So, Eloquent probably got your foreign key name wrong so you must override the foreign key by passing additional arguments to the hasMany/belongsTo method:
public function pasiens(){
return $this->hasMany('RegistrasiPasien','kelurahan_id');
}
public function kelurahan(){
return $this->belongsTo('DataKelurahan','kelurahan_id');
}

How to link data from one table to another using Laravel 4?

I have a customers migration table
Schema::create('customers', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('address');
$table->string('phone');
$table->string('email');
});
and a tripsheet migration table which goes like this
Schema::create('tripsheets', function(Blueprint $table) {
$table->increments('id');
$table->integer('tripsheet_num');
$table->integer('customer_id');
$table->string('date');
$table->string('customer_name');
$table->string('customer_address');
$table->string('customer_phone');
$table->string('rep_address');
$table->string('rep_phone');
});
I have also created a customer eloquent,
class Customer extends Eloquent {
public $timestamps = true;
public $table = 'customers';
protected $fillable = ['name', 'address', 'phone', 'email'];
public function tripsheets(){
return $this->belongsTo('Tripsheet', 'name', 'address', 'phone');
}
}
and a tripsheet model,
class Tripsheet extends Eloquent {
public $timestamps = true;
public $table = 'tripsheets';
protected $fillable = [];
public function customer(){
return $this->hasMany('Customer', 'name', 'address', 'phone');
}
}
and my routes.php goes like this,
Route::get('/', function()
{
return View::make('hello');
});
Route::controller('/customers', 'CustomerController');
Route::controller('/tripsheets', 'TripsheetController');
I would like to link the customer_name, customer_address, customer_phone from the tripsheet table to the name, address, phone of the customer table. I also want to know how to route them and fetch them as a json data to be used by angular JS to display the result.
now should i create a third table to link these two? Or should i call it with Customer::with('tripsheets')->all();in the routes/controller?
I also want to know how to route them and fetch them as a json data to be used by angular JS to display the result.?
I overlooked your code and after user315.. answer I see the problem. The belongsTo and hasMany has wrong arguments.
You need to change your code to the following to make it work:
class Customer extends Eloquent {
public function tripsheets(){
return $this->belongsTo('Tripsheet', 'tripsheet_num');
}
}
class Tripsheet extends Eloquent {
public function customer(){
return $this->hasMany('Customer', 'tripsheet_num');
}
}
The problem is that you have the field tripsheet_num in the table tripsheets. Laravel tries to find a field called tripsheet_id inside the tripsheets table when you use the belongsTo(Tripsheet) on Customer. In your case this key field is named different and is not found, and so the relation is not set.
Same goes for hasMany() only then it looks in the other table for the key field.
See the relation documentation for more info: One-to-one relation & hasMany
You only need the first argument in your belongsTo and hasMany method. The others are likely the reason why it's not working. They are meant for telling Eloquent what the foreign key is, and if you are specifying the foreign key as name, then it's obviously not going to work right.
Since you have proper naming conventions, Eloquent can accurately guess what they should be and you shouldn't need them.

Categories