Lightouse PHP Graphql - How to create a relationship? - php

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
...
🤦‍♂️

Related

I'm getting an error when trying to save a new model with 2 fields referencing different ids in the same table

I'm new to laravel, and I've picked up the basic workflow of creating, updating and deleting database entries using migrations, models and controllers. But now I'm trying to do the same with a subscriptions table that has a subscriberId and a followeeId in it. Both of these fields reference different ids of the same table (users). This kind of task seem to require some finetuning. And I'm stuck.
Here's my code with some comments.
Subscriptions Table
Schema::create('subscriptions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('subscriberId');
$table->unsignedBigInteger('followeeId');
$table->foreign('subscriberId')->references('id')->on('users');
$table->foreign('followeeId')->references('id')->on('users');
});
Previously, I've used another approach to foreign ids, namely the one with the $table->foreignId('user_id')->constrained() pattern, but in this particular case I need to make sure that the two foreign ids reference different users, so I went for a more verbose option.
User Model
public function subscriptions()
{
return $this->hasMany(Subscription::class, 'subscriberId');
}
Here I've added the second parameter. This seems to work.
Subscription Model
class Subscription extends Model
{
use HasFactory;
protected $fillable = [
'subscriberId',
'followeeId'
];
public function subscriberId()
{
return $this->belongsTo(User::class, 'id', 'subscriberId');
}
public function followeeId()
{
return $this->belongsTo(User::class, 'id', 'followeeId');
}
}
Here I pass additional parameters, too, although in this case I'm not so sure if these are the correct ones. But this is my best guess. If I'm not mistaken, the second parameter of the belongsTo relation is inferred from the model that is being passed in, not the model of the parent class as is the case with the hasMany relation. So in this case that would be 'id' of the users table, which would be the default here anyway, but I need the third parameter, so I explicitly state the second parameter as well. Again, I'm not sure about this combination, but that's what I was able to make of the docs. I've also used other combinations of additional parameters, and even tried getting rid of these two public functions altogether, but that won't work either.
Now, here's the controller. If I do this:
$user->subscriptions()->get();
I do get the subscriptions I want. But if I do this instead:
$user->subscriptions()->create([
'subscriberId' => 1,
'followeeId' => 2
]);
I get the 500 error. I've also tried another approach:
$newSub = new Subscription;
$newSub->subscriberId = 1;
$newSub->followeeId = 2;
$newSub->save();
return $newSub;
But still no success. I still get the 500 error when I try to save()
Please help me out.
Solution
I should have used
public $timestamps = false
in the Subscription model, and I also misunderstood the docs. The correct combo is
User Model
public function subscriptions()
{
return $this->hasMany(Subscription::class, 'subscriberId');
}
and
Subscription Model
public function subscriberId()
{
return $this->belongsTo(User::class, 'subscriberId');
}
public function followeeId()
{
return $this->belongsTo(User::class, 'followeeId');
}

Many to Many Relationship with 2 FK referencing from same PK Laravel 8

Hello I am working on a Laravel project, that i have to assign for one Mentorship «Mentoria», one Mentor «Mentor» and one student «Mentorando». The data of the student and the mentor, came from the Users table (i assigned them roles, using Spatie) , and the other table is called «Mentoria» Since there exists a many to many relation i created the pivot table that is called «utilizador_mentoria» and has ID_Mentor, ID_Mentorando (both are FKs coming from the users table),and ID_mentoria (coming from Mentoria table). I defined both models as this:
User Model:
protected $casts = [
'email_verified_at' => 'datetime',
];
public function interesses(){
return $this->belongsToMany(AreaInteresse::class, 'utilizador_interesse', 'id_utilizador', 'id_interesse');
}
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentoria', 'id_mentorando', 'id_mentor');
}
ps: I have interesses function with other model, that is working properly. my problem is with the «mentorias»
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentor','id_mentorando','id_mentoria');
}
With this, i am trying to get the data from all Mentorias, and the data of the Mentor that is assigned to that that Mentoria, however when i am doing this code on the controller, the data coming from the user appears empty, despite i have the DB filled with data. I tried a echo for testing, and it only shows the data of the Mentoria, and where it should appear the data of the Mentor assigned to that MEntoria, it is empty
the code from the controller:
public function mentorias(){
$mentorias = Mentoria::with('users')->get();
echo $mentorias;
return view('admin/mentorias/admin_mentorias', ['mentorias' => $mentorias]);
}
the output of the echo
[{"id":2,"titulo":"teste","titulo_en":"test","descricao":"fe","descricao_en":"ewfwe","created_at":"2021-12-28T01:32:10.000000Z","updated_at":"2021-12-28T01:32:10.000000Z","users":[]}]
Since as i already said, i already used data from 2 tables with Many to Many relation, however with only 1 FK per PK, and it is working properly, i have no idea why it is not working this way . I already checked for similar questions, however with no luck
Edit:
For testing purposes, i removed the column of one of the two FK that reference from the same PK, and i managed to work, however with this aditional FK i am not managing to make it work . I believe that the problem is with the relation, in the models but i have no idea how to make it work
I rearranged the funcitons in the models as they are now
User Model:
public function mentorias(){
return $this->belongsToMany(Mentoria::class, 'utilizador_mentoria', 'id_mentor', 'id_mentorando', 'id_mentoria');
}
Mentoria Model:
public function users(){
return $this->belongsToMany(User::class, 'utilizador_mentoria','id_mentoria','id_mentorando','id_mentor');
i also tried to took out,example «id_entorando» from the main () and put it after with the «withPivot» method, but it still didn't worked
I don't know if I properly understood the problem but your relationship is not a single "many to many", but two "one to many". In the end I'll show why it's convenient to consider them as two separated relationships.
First: If a user can have multiple mentorships but a mentorship can have only 1 mentor (which I suppose is what's happening), then you should use the "hasMany/belongsTo" pair:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
Second: Complete the scenario with the other relationship bewteen the mentorship and the students:
User Model:
public function mentorias(){
return $this->hasMany(Mentoria::class);
}
public function mentoria(){
return $this->belongsTo(Mentoria::class);
}
Mentoria Model:
public function user(){
return $this->belongsTo(User::class);
}
public function mentorandos(){
return $this->hasMany(User::class);
}
There should be a user_id column in the mentorias table that represents the Teacher which the mentorships belong to:
Schema::table('mentorias', function (Blueprint $table) {
$table->unsignedInteger('user_id')->nullable();
});
And there should also be a mentor_id column in the users table that represents the mentorship which the students belong to:
Schema::table('users', function (Blueprint $table) {
$table->unsignedInteger('mentoria_id')->nullable();
});
Third: This double representation of the User Model could lead to confusion, since two different objects (teacher and student) are using the same model but are not meant to have both relationships simultaneously: A teacher shouldn't be mentored and a student shouldn't lead mentorships.
In order to roperly manage Teachers and Students and prevent confusion, you could create additional models that inherit the User Model and define the relationships for them (instead of the User Model), so you can limit the fields and the relationships for each one of them.
User Model:
// No relationships
Teacher Model:
// Extending from User allows you to have all the User Model functionality
class Teacher extends User
{
public function mentorias(){
return $this->hasMany(Mentoria::class)->with('students');
}
}
Student Model:
// Extending from User allows you to have all the User Model functionality
class Student extends User
{
public function mentoria(){
return $this->belongsTo(Mentoria::class)->with('teacher');
}
}
Mentoria Model:
public function teacher(){
return $this->belongsTo(Teacher::class);
}
public function students(){
return $this->hasMany(Student::class);
}
In the end, you'll have all the info you need from the mentorship when you call objects like this:
Teacher::with('mentorias')->get();
// This will show the Teacher's mentorias and the students in each one
Student::with('mentoria')->get();
// This will show the Student's mentoria and its teacher
Mentoria::with(['teacher', 'students'])->get();
// This will show the teacher and the students for each mentoria

Laravel get model with relation's pivot condition

I need to get all appeals, that have appeal_stage.expiration_date less than NOW().
Now I have following solution:
public function scopeExpired($query) {
$query->join('appeal_stage', 'appeals.id', 'appeal_stage.appeal_id')
->where('appeal_stage.expiration_date', '<=', new Expression('NOW()'));
}
but resulted model dump shows that joined table is recognized as pivot table:
So, I want to ask - Is there some more convenient way to perform this request?
My suggestions is use Illuminate\Database\Eloquent\Relations\Pivot somehow, bu I do not quiet understand, how Pivot can be used here.
UPD 1
Models has next relations:
public function stages()
{
return $this->belongsToMany(Stage::class)->withPivot('prolongated_count', 'expiration_date')->withTimestamps();
}
public function appeals() {
return $this->belongsToMany(Appeal::class);
}
You should be able to do something like this:
$appeal->stages()->wherePivot('expiration_date', '<', $now)->get()
You should create relationship in appeal model
public function stages()
{
return $this->belongsToMany(Stage::class,'appeal_stage','appeal_id','stage_id')->wherePivot('expiration_date','<',Carbon::now())->withTimestamps();
}
In belongs To Many relationship second argument is your Pivot table name

Laravel 5 Eloquent-BelongsTo hasMany give me empty object

I come here cause i have an issue with Eloquent relations One To Many, i've tested a lot since a week now but nothing to do. I try here for my last chance, after this, i will try Database: Query Builder, but i will prefer to perform my code with Eloquent.
To make simple, i want to show the differents furnitures use with the id task, so a task hasMany furnitures.
So i give you the problem, i made the relation into my model, when i call the function HasMany, it's returned to me an empty object...
I use php artisan tinker on git bash but after a thousand of try, it's not working.
I show you my try with GitBash and after my code.
When i do this ">>> $comments = App\BonIntervention::find(2003);"
Tinker show me the right thing, the id "2003" to my Table related to my model, with all his components. But when i do this after:
"$comment = $comments->bonInterventionFournitures"
I'have this answer "Illuminate\Database\Eloquent\Collection {#810
all: [],
}"
And for a lot of try with different name/method his always the same, like my table are empty, but they're not....
Here you have my model for the "task", When i read the doc Laravel eloquent HastoMany, it appears that you can pass to the return, for the first parameter the namespace, for the second parameter the foreign key of the table, and for third parameter the primary key. So i did like this, i hope i didn't mistake myself.
namespace App;
use Illuminate\Database\Eloquent\Model;
class BonIntervention extends Model
{
protected $primaryKey = 'Id_TTa';
protected $table = 'T_Taches';
public $timestamps = false;
public function bonInterventionFournitures(){
return $this->hasMany('App\BonInterventionFournitures', 'Id_TTa', 'Id_TTaDFo');
}
}
Now it's the model for furnitures. (Same method for the parameters in the return)
namespace App;
use Illuminate\Database\Eloquent\Model;
class BonInterventionFournitures extends Model
{
protected $table = 'T_Taches_Details_Fournitures';
protected $primaryKey = 'Id_TTaDFo';
public function bonInterventions(){
return $this->belongsTo('App\BonIntervention', 'Id_TTa', 'Id_TTa');
}
}
I can show you my migrations for this table.
First, the table task (i not show you the entire table, because it's a lot of information.
public function up()
{
Schema::create('T_Taches', function(Blueprint $table)
{
$table->integer('Id_TTa', true);
$table->string('Responsable_TCa')->nullable()->index('Responsable_TCa');
$table->dateTime('Date_TTa')->nullable()->default('0000-00-00 00:00:00');
$table->string('Ste_TCl')->nullable()->index('Ste_TCl');
$table->string('Ste_Utl')->nullable()->index('Ste_Utl');
$table->string('Adr_Liv_TCl')->nullable()->index('Adr_Liv_TCl');
$table->string('Contact_TCo')->nullable()->index('Contact_TCo');
$table->string('Collaborateur_TCa')->nullable()->index('Collaborateur_TCa');
$table->string('NDevis_TDv')->nullable()->index('NDevis_TDv');
$table->string('Devis_Type_TTa')->nullable();
$table->string('NCommande_TDv')->nullable()->index('NCommande_TDv');
$table->dateTime('Date_Debut_TTa')->nullable();
$table->dateTime('Date_Fin_TTa')->nullable();
$table->dateTime('Date_Demande_TTa')->nullable();
Now the furnitures table
public function up()
{
Schema::create('T_Taches_Details_Fournitures', function(Blueprint $table)
{
$table->integer('Id_TTaDFo', true);
$table->integer('Id_TTa')->nullable()->index('Id_TTa');
$table->string('Class_TTaDFo')->nullable();
$table->string('Des_TTaDFo')->nullable();
$table->string('Ref_TTaDFo')->nullable();
$table->float('Qte_TTaDFo', 10, 0)->nullable()->default(0);
$table->float('PAHT_TTaDFo', 10, 0)->nullable()->default(0);
$table->float('Tx_Mge_TTaDFo', 10, 0)->nullable()->default(1.5);
$table->float('Vente_HT_TTaDFo', 10, 0)->nullable()->default(0);
});
}
I hope you understand my question, and thanks all for watching.
SOLVED
I solved this and i explain why on the answer, after my post.
I've found the solution. In fact in my models i erase the second and third parameters.
return $this->belongsTo('App\BonIntervention', 'Id_TTa', 'Id_TTa');
return $this->hasMany('App\BonInterventionFournitures', 'Id_TTa', 'Id_TTaDFo');
I just add the second parameter in my return hasMany, just like this
return $this->hasMany('App\BonInterventionFournitures', 'Id_TTa');
return $this->belongsTo('App\BonIntervention');
Now with artisan tinker when i do this
$comments = App\BonIntervention::find(2003);
$comment = $comments->bonInterventionFournitures;
I find the right answer.

Dealing with Eloquent relationships in Laravel 5.3

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

Categories