In Laravel, I am creating a message thread feature. My schema looks like this:
MessageThreads Table
column
id
MessageThreadParticapants Table
column
thread_id
user_id
And I have the corresponding models of MessageThread and MessageThreadParticapant. In the MessageThread model, I have the following relation:
public function users() {
return $this->hasMany(MessageThreadParticapant::class, 'thread_id', 'id');
}
Here is where things get funny. If I do:
MessageThread->users
I get an empty result. But if I do:
MessageThreadParticapant::where('thread_id', $same_thread_id)->get()
I get the correct amount of results back. What am I doing wrong here?
UPDATE
One of the suggestions was "hasMany(Model, 'foreign_key', 'local_key')" to be incorrect. Some more context,its failing my unit tests. I'm testing up a test as such:
public function testUsers() {
$thread1 = MessageThread::factory()->create();
$thread2 = MessageThread::factory()->create();
$this->assertCount(0, $thread1->users);
$this->assertCount(0, $thread2->users);
$user1 = User::factory()->create();
$user2 = User::factory()->create();
$user3 = User::factory()->create();
$user4 = User::factory()->create();
MessageThreadParticapant::factory()->create([
'user_id' => $user1->id,
'thread_id' => $thread1->id
]);
MessageThreadParticapant::factory()->create([
'user_id' => $user2->id,
'thread_id' => $thread1->id
]);
MessageThreadParticapant::factory()->create([
'user_id' => $user2->id,
'thread_id' => $thread2->id
]);
MessageThreadParticapant::factory()->create([
'user_id' => $user3->id,
'thread_id' => $thread2->id
]);
MessageThreadParticapant::factory()->create([
'user_id' => $user4->id,
'thread_id' => $thread2->id
]);
//PASSES!!!!
$this->assertCount(2, MessageThreadParticapant::where('thread_id', $thread1->id)->get());
//FAILS!!!
$this->assertCount(2, $thread1->users);
$this->assertCount(3, $thread2->users);
}
At bottom of my test:
//PASSES!!!!
$this->assertCount(2, MessageThreadParticapant::where('thread_id', $thread1->id)->get());
//FAILS!!!
$this->assertCount(2, $thread1->users);
In other tests, $thread->users works correctly in getting the right amount of users back. Why are these getting different results?
I solved this problem in two steps.
Refresh
Lazy loading apparently only represents that state of the object at the time that it was loaded. Meaning it's not retrieving new data from the DB when the joined property is called. To solve, just do a refresh on the model and then access the joined property.
$model->refresh();
$model->users;
String ID
I'm using UUID in Postegresql. Even those I am using $cast = ['id' => 'string']; in model, this is not enough. I also have to add:
protected $keyType = 'string';
Related
I have a form which takes in the following values: stage_name, stage_type, 'client_id', 'created_at', 'updated_at'
I was able to create a method for adding a new item in the table, however in attempting to update it I came across this error:
"SQLSTATE[HY000]: General error: 1364 Field 'client_id' doesn't have a default value (SQL: insert into stages (updated_at, created_at) values (2020-05-21 02:43:53, 2020-05-21 02:43:53))"
My controller update function:
public function update(Request $request, Stage $stage)
{
$request->validate([
'stage_name' => 'required|max:300',
'stage_type' => 'required'
]);
$client = Auth::user()->client_id;
$stage->update([
'stage_name' => $request-> stage_name,
'stage_type' => $request->stage_type,
'client_id' => $client,
]);
$stage->save();
return $stage;
}
Even if I directly define client_id => 1 it still yields the same error
You are using empty save() function $stage->save(); and you are not defining the record which should update. Simply use:-
$stage->where($column,$value) //othervise it will update all the records.
->update([
'stage_name' => $request-> stage_name,
'stage_type' => $request->stage_type,
'client_id' => $client,
]);
This code is sufficient to update the recorded not need to add save(). If you wanna using save then follow the below code.
$stage = $stage->find($id);
$stage->stage_name = $request->stage_name;
$stage->stage_type = $request->stage_type;
$stage->client_id = $request->client;
$stage->save()
You mixed the both approaches.
I'm learning Laravel and have created a public endpoint where I want to output only certain information of some comments if a user is not authenticated from a GET request.
I have managed to filter out the comments based on whether or not they are approved. I now want to filter out the data that is returned. I have attached a screenshot of what is currently returned.
Ideally, I only want to return the id, name and the body in the json. How can I go about this? I tried the pluck() method which did not give the desired results. Any pointers would be greatly appreciated
public function index(Request $request)
{
if (Auth::guard('api')->check()) {
return Comment::all();
} else {
$comments = Comment::where('approved', 1)->get();
return $comments->pluck('id','name','body');
}
}
To select the particular columns, you can pass columns name to get as
$comments = Comment::where('approved', 1) -> get(['id','name','body']);
You can use a transformer to map the incoming data to a sensible output based on the auth state. The following example comes from the Fractal lib:
<?php
use Acme\Model\Book;
use League\Fractal;
$books = Book::all();
$resource = new Fractal\Resource\Collection($books, function(Book $book) {
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
'author' => [
'name' => $book->author_name,
'email' => $book->author_email,
],
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
]
];
});
Ideally, you would create 2 classes that extend from Transformer and pass the correct one to the output.
If you want to pass the result as json respose
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return Response::json($comments);
If you want to pass the result as to blade
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return view('your_blade_name')->with('comments',$comments);
I am trying to create an associated table, where I've exam_id and question_id that are linked with belongsToMany relationship through an ExamsQuestions table that has belongsTo relationship on both of them.
I've created the (atleast some of the) relations correctly, as I can save the ids correctly, but I've in the associative table also fields "questionPoints" and "isActive" fields, but they're not updating.
I use .js file to send a run-time request.
But when I'm getting response from the controller function, the joindata is not set to the objects correctly.
in Database the rows are not updated at all, even though some of the ._joindata information is applied (ids). I think this is CakePHP's built-in association that does the joining, as 1 level of associations is applied by default.
In ExamsController I first send data to the view, in order to render the view correctly. After that I use the request data to patch entity and save the entity.
public function take($id)
{
if ($this->request->is('get'))
{
$exam = $this->Exams->get($id, [
'contain' => ['ExamTemplates' => ['Questions.Answers']]
]);
$users = $this->Exams->Users->find('list', ['limit' => 200]);
$examTemplates = $this->Exams->ExamTemplates->find('list', ['limit' => 200]);
$this->set(compact('exam', 'users', 'examTemplates'));
}
if ($this->request->is('post')) {
$exam = $this->Exams->get($id, ['contain' => ['Questions']]);
$this->autoRender = false;
$exam = $this->Exams->patchEntity($exam, $this->request->data);
if ($this->Exams->save($exam)) {
$response = [
'success' => true,
'message' => __("exam updated"),
'this' => $this->request->data,
];
$exam_id = $_POST['id'];
$this->Flash->success(__('The exam has been updated.'));
} else {
$response = [
'success' => false,
'error' => $exam->errors(),
'request' => $this->request->data,
'message' => __("Error creating template")
];
}
$this->response->body(json_encode($response));
}
}
Thanks.
Okay, I fixed the problem, but I don't know if the fix was done the most correct way.
Instead of updating the field in AJAX call, I changed the _joinData update to happen in the controller based on request data.
So I just declared a new _joinData as new array and gave it the fields that were not automatically added. One thing to note is that if I gave it any field manually that CakePHP does autocomplete, it would also not update.
I have mysql table 'test' with three columns,
1.sno 2.name 4.country
this code is easily understandable
$person = \App\Test::find(1);
$person->country; //Defined in Test eloquent model
now i want to do something like this:
$p = ['sno' => 1, 'name' => 'Joe', 'country' => '1' ];
$p->country; //Return relevent column form countries table as defined in Model
The thing to remember is that the user i am trying to map is already present in the database table. How to i convert an array to eloquent model?
You could instantiate the model class with no attributes:
$dummy = new \App\Test;
Then you can call the newInstance() method:
$attributes = ['sno' => 1, 'name' => 'Joe', 'country' => '1' ];
$desiredResult = $dummy->newInstance($attributes, true);
The true flag in the method is telling eloquent that the instance already exists in database, so you can continue working with it normally. Now you can do:
$desiredResult->country //'1'
I'm trying to insert into the table of a model with multiple levels of HasMany relationships. Here's the breakdown so far
Customer->(HasMany)->Members->(HasMany)->Incomes
However, on when trying to insert into the Incomes table, I get a "Not null violation" with the foreign key from the Members table not being carried to Incomes. I know the most common problem is screwing up the $_has_many and $_belongs_to properties, but as far as I can tell they are fine. Plus, Just inserting into the Member table works fine so I know at least for the first layer it's working! The only thing I can think of is if since it's a second level down, it's screwing up because of that. Here's my code:
Relation Link (Member)
protected static $_has_many = array(
'incomes' => array(
'key_from' => 'id',
'model_to' => 'Model_Income',
'key_to' => 'member_id',
'cascade_save' => true,
'cascade_delete' => true,
),
);
Relation Link (Income)
protected static $_belongs_to = array(
'member' => array(
'key_from' => 'member_id',
'model_to' => 'Model_Member',
'key_to' => 'id',
'cascade_save' => true,
'cascade_delete' => true,
),
);
The Controller Code
// code to set up $customer
$customer->members[] = Model_Member::forge();
// set $member_vals here
$customer->members[0]->set($member_vals);
$customer->members[0]->incomes[] = Model_Income::forge();
// set $income_vals here
$customer->members[0]->incomes[0]->set($income_vals);
$customer->save();
The first problem that comes to mind is that you're setting relations on ->members and ->incomes (plural), instead of ->member and ->income (singular).
I also prefer to do relations separately, instead of chaining them like you do. So, if you already have relations set when you're adding a new Member to Customer, or a new Income to Member, my guess is that the index will not be 0.
What if you try the following instead:
$member = Model_Member::forge($member_vals);
$income = Model_Income::forge($income_vals);
$member->income[] = $income;
$customer->member[] = $member;
$customer->save();
Does it work?
Different suggestion:
Since it appears that you're following the table/column naming convention, try only this:
// Model_Member
protected static $_has_many = array(
'incomes'
);
// Model_Income
protected static $_belongs_to = array(
'members' // plural, not singular like you seem to have
);
If you want more help, you should definitely show how you have the Member/Income models and tables.