Laravel: ORM retrieving data ordered by related table - laravel

I am creating simple messenger for Laravel 4, with fuctionality pretty same as Facebook messenger. I have problem making Eloquent query to list latest messages with threads for currently logged user.
Consider I have the following DB tables for messenger (there is also users table):
+-----------+ +--------------+ +----------------+ +----------------+ +-----+
|threads | |thread_users | |messages | |messages_status | |users|
+-----------+ +--------------+ +----------------+ +----------------+ +-----+
|id | |id | |id | |id | |id |
|title | |thread_id | |thread_id | |user_id | |name |
|created_at | |user_id | |sender_id (user)| |message_id | +-----+
|deleted_at | |created_at | |content | |deleted_at |
+-----------+ |deleted_at | |created_at | +----------------+
+--------------+ +----------------+
DB explanation:
When user writes first message he creates thread. All users added to thread are listed in thread_users table. Message submitted to thread are stored in messages table, which is linked to messages_status table where i'm storing info if user deleted particular message from thread for his account.
Laravel models (i have removed SoftDeletingTrait to make it clear):
class threads extends \Eloquent {
protected $table = 'threads';
public function participants() {
return $this->hasMany('messengerThreadUsers', 'thread_id', 'id');
}
public function userParticipation() {
return $this->hasOne('threadUsers', 'thread_id', 'id')->where('user_id', '=', Auth::user()->id);
}
public function messages() {
return $this->hasMany('message', 'thread_id', 'id');
}
public function lastMessage() {
return $this->hasOne('message', 'thread_id', 'id')->latest();
}
}
class threadUsers extends \Eloquent {
protected $table = 'thread_users';
}
class message extends \Eloquent {
protected $table = 'messages';
public function statuses() {
return $this->hasMany('messageStatus', 'message_id', 'id');
}
public function thread() {
return $this->hasOne('threads', 'id', 'thread_id');
}
}
class messageStatus extends \Eloquent {
protected $table = 'messages_status';
public function message() {
return $this->hasOne('message', 'id', 'message_id');
}
public function thread() {
return $this->hasOne('threads', 'id', 'thread_id');
}
}
Now i want to make query that return 5 most recent messages (ordered by message creation date) together with threads, for currently logged user, but where message_status.deleted_at is NULL.
So far i've got this:
$this->threads->select(\DB::raw('threads.*'))
->with('lastMessage')
->has('userParticipation')
->join('messages', 'messages.thread_id', '=', 'threads.id')
->orderBy('messages.created_at', 'DESC')
->paginate(5);
But it keeps returning me dupicate threads. In DB i have 2 threads:
thread #1 have 2 messages in it
thread #2 have 1 message in it
output below:
0 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 1' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:20:03' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 1' (length=129)
'created_at' => string '2015-07-07 19:36:52' (length=19)
1 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 1' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:20:03' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 1' (length=129)
'created_at' => string '2015-07-07 19:36:52' (length=19)
2 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 2' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:25:13' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 2' (length=129)
'created_at' => string '2015-07-07 19:56:22' (length=19)

Related

How to create the data into database using laravel

I want to do the registration form and insert the details into two tables which are the company table and the people table. I face the problems here:
How can I get the comp_id and add it into my master_id? The comp_id is AUTO_INCREMENT from PHPMYADMIN.
How to add the people_id into the company table after the people table is created?
company table
| comp_id| people_id| master_id|
|:---- |:------:| -----:|
| 7| 112| 7|
| 8| 113| 8|
people table
| people_id| office_id| username|
|:---- |:------:| -----:|
| 112| 7| person A|
| 113| 8| person B|
Here is my code, but there is an error and it is unable to create the database.
protected function create(array $data)
{
$company = Company::create([
'people_id' => '["'.$people->person_id.'"]',
'master_id' => $company->company_id,
]);
$people = Person::create([
'username' => $data['name'],
'office_id' => $company->comp_id,
]);
}
Does anyone know how to solve it?
protected function create(array $data)
{
$company = Company::create([
'master_id' => $company->company_id,
]);
$people = Person::create([
'username' => $data['name'],
'office_id' => $company->comp_id,
]);
$company->people_id = $people->person_id;
$company->save();
}

Codeigniter 4 Undefined index: user_id

I am trying to show display a "join" result and only want to show the user_id, username, email only once for users table even if there are many records on the other table, so I tried to make a query builder like below:
Table users | Table add_game
|
user_id | username | email | game_id | user_id | ign | acc_id
1 | userA | userA#email.com | 1 | 1 | ignA | accA
2 | userB | userB#gmail.com | 2 | 1 | ignB | accB
| 1 | 2 | ignB | accB
| 3 | 2 | ignD | accD
Model :
<?php namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class LoginModel{
protected $db;
public function __construct(ConnectionInterface &$db){
$this->db =& $db;
}
public function login(string $str)
{
return $this->db->table('users')
->groupStart()
->where('username', $str)
->orWhere('email', $str)
->groupEnd()
->join('add_game', 'add_game.user_id = users.user_id')
//->distinct('users.user_id')
//->select(("GROUP_CONCAT(game_id, ign, acc_id) AS userdata"))
->get()
->getResultArray();
}
}
Controller :
public function login()
{
$data = [];
helper(['form']);
$validation = \Config\Services::validation();
$db = db_connect();
$model = new LoginModel($db);
$user = $model->login($this->request->getVar('userlogin'));
$this->setUserSession($user);
echo view('templates/header', $data);
echo view('account/login', $data);
echo view('templates/footer', $data);
}
private function setUserSession($user){
$data = [
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => $user['email'],
'firstname' => $user['firstname'],
'lastname' => $user['lastname'],
'dob' => $user['dob'],
'country' => $user['country'],
'country_code' => $user['c_code'],
'contact' => $user['contact'],
'game_id' => $user['game_id'],
'ign' => $user['ign'],
'acc_id' => $user['acc_id'],
'isLoggedIn' => true
];
session()->set($data);
return true;
}
But right now I am getting
Undefined index: user_id
error message. Previously there was no issue or error when I was using without query builder for my login :
public function login(string $str, string $fields, array $data)
{
return $this->where('username', $data['userlogin'])->orWhere('email', $data['userlogin'])
->first();
}
How to resolve this error?
As by your comment (image) your array looks like:
Array
(
[0]=>Array
(
[user_id]=>1,
[user_name]=>'test',
//etc.
)
)
You get the
Undefined index: user_id
error message, because of addressing wrongly the array while using 'user_id' => $user['user_id']
the correct way is to add the index you want to retrieve like:
$this->setUserSession($user[0]); // where 0 can be changed to the index you pretend
now the array is flattened and 'user_id' => $user['user_id'] doesn't throw an error anymore.

How to fix 'Object of class stdClass could not be converted to string' when using collection diff function

I'm having this problem when I try to use the function of collections diff on two collections that I've created from laravel DB, they have the same structure :
$diff = $reservations->diff($pitches);
the two collection from a dump :
/home/vagrant/projets/fffff/app/Http/Controllers/API/SizeController.php:126:
object(Illuminate\Support\Collection)[266]
protected 'items' =>
array (size=6)
0 =>
object(stdClass)[271]
public 'id' => int 1
1 =>
object(stdClass)[265]
public 'id' => int 2
2 =>
object(stdClass)[270]
public 'id' => int 3
3 =>
object(stdClass)[272]
public 'id' => int 4
4 =>
object(stdClass)[276]
public 'id' => int 5
5 =>
object(stdClass)[275]
public 'id' => int 6
And
/home/vagrant/projets/fffff/app/Http/Controllers/API/SizeController.php:127:
object(Illuminate\Support\Collection)[274]
protected 'items' =>
array (size=1)
0 =>
object(stdClass)[282]
public 'id' => int 1
Be careful when comparing an Illuminate/Database/Eloquent/Collection with an Illuminate/Support/Collection, the diff() function works differently in each of them.
In the given case I would pluck the unique property from the second collection and use whereNotIn.
$diff = $reservations->whereNotIn('id', $pitches->pluck('id'));

How to import a single excel file/sheet with various Models (and sub-data/models) in Laravel?

As per the title, I have a single excel file full of data (parent/child) in each row. The Laravel Excel site shows how to import, assuming a single model per row - but how does one import the child data?
https://laravel-excel.maatwebsite.nl/3.1/imports/
eg 'pseudo' schema:
Schema::create('students', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->string('student_code',16);
$table->string('student_name',64);
$table->string('student_surname',64);
});
Schema::create('student_courses', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->integer('student_id')->unsigned()->index();
$table->string('course', 32);
$table->date('start_date');
$table->date('end_date');
$table->timestamps();
});
Schema::create('student_contacts', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->integer('student_id')->unsigned()->index();
$table->string('contact_name', 32);
$table->string('contact_number', 32);
$table->timestamps();
});
Eg File: students.xlsx
Student Code | Student Name | Student Surname | Course | Course Start Date | Course End Date | Contact Person | Contact Numbers
ABC1 | Arnold | Clarp | C++ | 2019-01-01 | 2019-12-01 | Boogle | 555-111-222
DEF2 | Delta | Flork | English | 2019-01-02 | 2019-12-02 | Google | 555-111-333
GHI3 | Goblin | Clark | Science | 2019-01-03 | 2019-12-03 | Foogle | 555-111-444
Assuming my import code:
class StudentsImport implements ToModel, WithStartRow
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new Student([
'student_code' => $row[1],
'student_name' => $row[2],
'student_surname' => $row[3],
]);
}
/**
* #return int
*/
public function startRow(): int
{
return 2;
}
}
Where would I actually go about plugging in the import of the "child" course/contact data?
I'm guessing that instead of 'returning a new student' - I would actually first assign it to a variable, then import it then? Is this the correct way?
eg:
public function model(array $row)
{
$student = new Student([
'student_code'=> $row[1],
'student_name'=> $row[2],
'student_surname'=> $row[3],
])
$student->courses()->create([
'course'=>$row[4],
'start_date'=>$row[5],
'end_date'=>$row[6]
]);
$student->contacts()->create([
'contact_name'=>$row[7],
'contact_number'=>$row[8]
]);
return $student;
}
//actual code as it stands, no longer 'pseudo':
$student = new Student([
'bursary_provider_id' => 1,
'bursary_provider_reference' => 'xxx',
'student_name' => $row[1],
'student_initials' => $row[3],
'student_surname' => $row[2],
'passport_number' => $row[7],
'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
'country_id' => 7,
'id_number' => $row[6],
'status' => $status,
'notes' => $row[5]
]);
if (isset($row[10])) {
$student->visas()->create([
'country_id' => 201,
'visa_reference_number' => $row[10],
'visa_expiration_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[11])
]);
Error (its not passing across the parent id)
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'student_id' cannot be null (SQL: insert into student_visas (country_id, visa_reference_number, visa_expiration_date, student_id, updated_at, created_at) values (201, ABCHFV4, 2019-12-31 00:00:00, , 2019-01-11 08:03:06, 2019-01-11 08:03:06))
You get the error because the student is not yet written to de database, so no id has been assigned to the new student instance. If you use collections the data is immediately written to the database. In the model function you can use
$student=Student::Create(['bursary_provider_id' => 1,
'bursary_provider_reference' => 'xxx',
'student_name' => $row[1],
'student_initials' => $row[3],
'student_surname' => $row[2],
'passport_number' => $row[7],
'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
'country_id' => 7,
'id_number' => $row[6],
'status' => $status,
'notes' => $row[5]]);
This will write the student to the database and generate the neccessary id. I am not sure how this affects performance though.
The answer, (and do not ask me why, because documentation is so poor at this stage) is to use collections.
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
and instead of ToModel:
public function collection(Collection $rows)
and it doesnt 'return' anything. Other than these changes, using the exact same code in OP works as intended.

Laravel Model nested query

I was trying to write a single query to get full user diet from database. The problem is that I am not able to get data from the diet_food table. Here are my tables:
diet_settings (id, total_days, etc). The basic information about the users diet.
eating_types (id, diet_id, eating_time, etc). Information about breakfast, lunch etc.
diet_food (id, eating_type_id, product, etc). Food related to each eating type.
This is the query that I use in my controller class:
$diet = Diet::with("eatingType.dietFood")->get()->find($id)->toArray();
So when I use var_dump() this is what it looks like:
array (size=7)
'id' => int 2
'user_id' => int 1
'total_days' => int 1
'total_eating' => int 6
'notes' => string '' (length=0)
'created' => string '2016-09-01' (length=10)
'eating_type' =>
array (size=3)
0 =>
array (size=6)
'id' => int 7
'diet_id' => int 2
'eating_time' => string '8:00' (length=4)
'eating_type' => string 'Breakfast' (length=10)
'recommended_rate' => int 0
'diet_food' =>
array (size=0)
...
1 =>
array (size=6)
'id' => int 8
'diet_id' => int 2
'eating_time' => string '12:00' (length=5)
'eating_type' => string 'Lunch' (length=14)
'recommended_rate' => int 0
'diet_food' =>
array (size=0)
...
In the array the diet_food is empty even though I have data stored in the datababase.
Diet Model
class Diet extends Model
{
public $table = "diet_settings";
public $timestamps = false;
public function eatingType(){
return $this->hasMany(EatingType::class);
}
}
EatingType Model
class EatingType extends Model
{
public $timestamps = false;
public $table = "eating_types";
public function dietFood(){
return $this->hasMany(DietFood::class, 'id','eating_type_id');
}
public function diet(){
return $this->belongsTo(Diet::class);
}
}
DietFood Model
class DietFood extends Model
{
public $timestamps = false;
public $table = "diet_food";
public function eatingType(){
return $this->belongsTo(EatingType::class);
}
}
Solved by following this guide about pivots: http://laraveldaily.com/pivot-tables-and-many-to-many-relationships/
Also when you are outputting data it should be done like this:
$diets = Diet::with('eating')->get();
foreach($diets as $diet){
var_dump($diet->eating->toArray());
}

Resources