Dealing with Eloquent relationships in Laravel 5.3 - php

I have the following tables created using Schemas:
Schema::create('typen', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
});
Schema::create('auktionen', function (Blueprint $table) {
$table->increments('id');
$table->integer('typ')->unsigned();
$table->foreign('typ')->references('id')->on('typen');
});
The table typen is only created once at contains fixed values:
Typen
id| name
1 | Typ 1
2 | Typ 2
...
Those values are constant, no more will be added during the application lifetime. So every Auktion I create should be associated with one Typ.
I created the model for Auktion and thought of it as a one-to-one relationship (is this correct?).
I would like to create an Auktion with a given Typ using Eloquent queries like this:
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->associate($thetyp);
and fetch the name of the Typ of my Auktion:
$auktion->typ->name;
Currently my model looks like this for Auktion:
public function typ()
{
return $this->hasOne('App\Typ', 'typ');
}
which is not working. I already tried setting different relationships but I just end in different error codes ranging from undefined methods (associate() to an error where an SQL statement failed trying to update my typen table (when using save() - which I really do not want to).
Can someone clarify this problem for me and explain which relationship I have to use?
EDIT1: As mentioned in a comment I already tried using belongsTo in Auktion model
public function typ()
{
return $this->belongsTo('App\Typ', 'typ');
}
which results in Undefined property: App\Auktion::$typ when calling
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->save($thetyp);
and when calling $auktion->typ->name; in
Trying to get property of non-object
EDIT2:
I just figured that
echo $auktion->typ()->first()->name;
is indeed working. But referring to this answer this should be the same as
echo $auktion->typ->name;
What exactly am i doing wrong?
EDIT3:
I tried using suggested code:
$thetyp = App\Typ::find($typ);
$auktion->typ->save($thetyp);
After I navigated to the view ehere I run the code I got this:
I got this the second time today, somwhow out of nowhere

Here is some code enhancement:
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->save($thetyp);
To:
//Will return null if Model is not found
$thetyp = App\Typ::find(1);
//You actually have to retrieve the relationship, because calling typ()
//will only retrieve the Relationship query
$auktion->typ()->get()->save($thetyp);
The problem is that the relationship is defined backwards. You need to make Auction belongTo Type, and change Type to hasMany Auctions. The statement would read:
"A Type has many Auctions. An Auction has one Type".
Here are the classes (in English, sorry, my German is bad :( so I just did it in English) with the migrations:
-Auction class:
class Auction extends Model
{
protected $table = 'auction';
public function type()
{
return $this->belongsTo('App\Type');
}
}
-Auction migration:
Schema::create('auction', function(Blueprint $table){
$table->increments('id');
$table->integer('type_id')->references('id')->on('type')->nullable();
$table->string('title')->nullable();
$table->string('description')->nullable();
$table->timestamps();
});
-Type class:
class Type extends Model
{
protected $table = 'type';
public function auction()
{
return $this->hasMany('App\Auction');
}
}
-Type migration:
Schema::create('type', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->timestamps();
});
First, you can create a Type object (or insert it with a query) so we can have a Type row that we can relate to an Auction object/entry, and do the following:
//Example of Type obj creation
$type = new Type();
$type->name = 'Type #1';
//Don't forget to save
$type->save();
//OR find/retrieve a Type obj
//Where $id is the id of the Type obj, else the obj will be null
$type = Type::find($id);
$auction = new Auction();
$auction->title = 'Title #1';
$auction->description = 'Test description';
$auction->type_id = $type->id;
//Don't forget to save
$auction->save();
Now later in your code, whenever you are using an Auction object and you want to retrieve the associated type (if any), you can use:
$type = $auction->type()->get();
Which will return the instance of Type, and you will be able to retrieve the property name like so:
$type->name
I hope this helps! Let me know if you have any more questions!

Your second edit makes a lot of sense. Your method name is typ but that's also the name of the column. So when you use $auktion->typ() it's actually loading the relationship. When you use $auktion->typ it's grabbing the value of that column.
You need to either continue working this way using the parenthesis to load the relation and no parenthesis to grab the value of the column, or you can change the name of the column to something better such as typ_id which is ultimately what Laravel expects it to be and should save you from more similar headaches down the road.

You can try this in your Auktion model
$thetyp = App\Typ::find(1);
$auktion->typ->save($thetyp);
Now fetch
$auktion->typ->name

Related

Laravel: matches array (Tinder clone) to my user returns empty

I'm making a Tinder clone to practice, I want to retrieve a user matches (when both users like eachother) but when I do:
$user = User::with('matches')->findOrFail(1);
The matches array returns empty, despite all users liking eachothers,
I created a Users_Users_liked pivot table:
Schema::create('users_users_liked', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id')->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->unsignedInteger('user_liked_id')->nullable()->index();
$table->foreign('user_liked_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->timestamps();
});
I set up the User model with relationship as follows (notice matches realtionship, which always returns an empty array)
public function likesToUsers()
{
return $this->belongsToMany(self::class, 'Users_Users_liked', 'user_id', 'user_liked_id');
}
public function likesFromUsers()
{
return $this->belongsToMany(self::class, 'Users_Users_liked', 'user_liked_id', 'user_id');
}
public function matches()
{
return $this->likesFromUsers()->whereIn('user_id', $this->likesToUsers->keyBy('id'));
}
In my UsersSeeder I give likes to user 1 from all users
$users = json_decode($json, true);
foreach ($users as $data)
{
$user = new User();
$user->name = $data['name'];
$user->save();
if($user->id > 1)
{
$user->likesToUsers()->attach([1]);
$user->save();
}
}
$user = User::findOrFail(1);
$user->likesToUsers()->attach([2,3,4,5,6,7,8,9,10,11]);
$user->save();
There are a couple of issues in your current matches relationship:
whereIn takes an array of ids (where the values of each item is the id), whereas, using keyBy will mean that the values are the instances of the model. pluck would have been the right method to use i.e. $this->likesToUsers->pluck('id').
The above would only have worked if you already had the model loaded so eager loading wouldn't have worked. There are a couple of reasons for this:
The models wouldn't have yet been loaded so you couldn't then call the likesToUsers relationship to actually retrieve the ids i.e. you can't use the value of one relationship in another relationship in this way.
Even if you were lazily eager loading the relationships, if you were loading multiple models, Laravel would actually only use the relationship values from the first model (I think it's the first) so the rests of the relationships would be wrong.
What you can do though is use the query side of a relationship to retrieve the data. The long and the short of it is don't try and use the value of one relationship in another when trying to eager load.
All that being said, one option would potentially be to join the pivot table on the relationship (again) and compared the opposite columns:
public function matches()
{
return $this->likesFromUsers()
->join('users_users_liked as alt_users_users_liked', function (JoinClause $join) {
$join
->whereColumn('users_users_liked.user_liked_id', 'alt_users_users_liked.user_id')
->whereColumn('users_users_liked.user_id', 'alt_users_users_liked.user_liked_id');
});
}
Some general notes/tips:
You can simplify your users_users_liked migration by using the foreignId() method instead:
$table->increments('id');
$table->foreignId('user_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate();
$table->foreignId('user_liked_id')->constrained('users')->cascadeOnDelete()->cascadeOnUpdate();
$table->timestamps();
It's also usually a good idea to add a unique constraint to your pivot tables (unless you potentially want duplicates):
$table->unique(['user_id', 'user_liked_id']);
Finally, it might be worth taking a look at Model Factories.

How to make composite/multiple primary key in Laravel >= 5.6?

I have a many-to-many relationship created between USERS and COURSES. The models and migrations are made as follows.
User Model:
public function courses()
{
return $this->belongsToMany(Course::class)->withTimestamps();
}
Course Model:
public function users()
{
return $this->belongsToMany(User::class, 'course_user', 'course_id', 'user_id')->withTimeStamps();
}
The problem is I cannot find a way to implement a composite primary key into my migration file or pivot table in Laravel like this,
Schema::create('course_user', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('course_id')->unsigned()->index()->primary();
$table->foreign('course_id')->references('id')->on('courses');
$table->integer('user_id')->unsigned()->index()->primary();
$table->foreign('user_id')->references('id')->on('users');
});
After coding the above, when I call php artisan migrate:refresh an error appears showing multiple primary keys performed in the linked table. So I did a research on this matter and found out that ELOQUENT does not support composite primary keys as stated in this forum: link to the forum
Is there any other way I can go around this?
I have somehow managed to write a piece of code to detect a existing entries in the pivot table or the many-to-many relationship, inside the controller and avoid new duplicate entries.
To counter attack this problem I wrote a piece code in the controller to detect an existing tuple in the pivot table as follows. NOTE: I have used Resources to retrieve data, you can avoid the Resource functions and use the normal method also.
The userId and courseId are passed as parameters in the request:
public function index(Request $request) {
$target = new AcademicPaymentResource(User::with('courses')->findOrFail($request['userId']));
if ($target != null)
{
foreach ($target as $query){
// search for existing tuples
foreach ($query->courses as $subQuery => $key){
// Check if courseId in existing tuple is equal to the courseId of request
if ( $query->courses[$subQuery]->pivot->course_id == $request['courseId']){
// Do anything here..
// display data to test if it works
return $query->courses[$subQuery];
}
}
// update the pivot table if the tuple doesnt exist
$user = User::findOrFail($request['userId']);
$user->courses()->attach($request['courseId']);
// read tuple data again to display if it works
$target = new AcademicPaymentResource(User::with('courses')->findOrFail($request['userId']));
return $target;
}
}
}
This method actually works fine, since I have used it flawlessly so far.
But if there is any other proper method please don't hesitate to answer..

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');
}

Lightouse PHP Graphql - How to create a relationship?

I'm struggling to understand how to create relationships through lighthouse. Let's say I have a Post model that belongsTo a User. In my schema.graphql, I have:
type Mutation {
createPost(input: CreatePostInput! #spread): Post! #create
}
input CreatePostInput! {
text: String!
user: BelongsToUser!
}
input BelongsToUser {
connect: ID!
}
And my Post object in the database has a foreign key column of user_id
When I run the query:
mutation {
createPost(input: {
text: "Hello World",
user: {
connect: 1
}
}){
id
}
}
I get an error: Field 'user_id' doesn't have a default value
I feel like I am doing something wrong where this mutation is not updating the correct column for the relationship, can someone please point me to what I might be doing wrong? (If I make the column nullable with a default value in the database, it is inserted but the relationship is of course incorrect. I can also adjust the graphql schema to use user_id: Int which does work but feels like the wrong way to do things.)
Everything works correctly via Eloquent, and graphql queries work, correctly pulling in related objects.
Note: As per the documentation, I have defined the return class of the relationship in my model:
class Post extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
and the relevant migration:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('text');
...
I'm not sure but are using a right Keys ?
In php my admin go to designer section and select the relation
if you got will get more clear error
to know more about designer follow this link
https://kb.bodhost.com/turn-the-designer-mode-on-in-phpmyadmin/
Ok, the problem was that although the return type was defined in the model, I failed to import it above:
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
public function user(): BelongsTo
...
🤦‍♂️

unable to get attributes in an accessor method - laravel eloquent

Okay I want to have custom field that does not exist as a column in my db table.
I followed, last part :
http://laravel.com/docs/4.2/eloquent#accessors-and-mutators
My model code:
class Car extends Eloquent{
protected $fillable = array('driverID', 'fuelRemaining');
protected $appends = array('is_driver');
public function user(){
return $this->belongsTo('user');
}
public function getIsDriverAttribute(){
return ($this->attributes['driverID'] == Auth::user()->id);
}
}
Car table:
Schema::create('cars', function(Blueprint $table)
{
$table->increments('id');
$table->integer('driverID');
$table->integer('fuelRemaining');
$table->mediumtext('desc');
$table->timestamps();
});
As you can see i want an extra field which is "is_driver" to be returned, but when I run this, this field is used to determine whether current signed in user is the driver himself by comparing the IDs.
it will output this error:
Undefined index: driverID
Not sure what am I doing wrong here, please advice.
Ah I have found why. This is a reference for future readers
In my controller I only get these two
$car = Car::where('fuelRemaining', 0)->get(array('id', 'desc'));
When i added authorID to the get array
$car = Car::where('fuelRemaining', 0)->get(array('id', 'desc', 'authorID'));
I am able to get the authorID attribute in my custom accessor mentioned in the question.

Categories