Codeigniter 4 Undefined index: user_id - query-builder

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.

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

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.

How to tell Validator to call another Rule checker

I have this table :
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
| id | rule_number | profile_id | dest_ip_type | dest_ip | dest_ip_mask | dest_iprange_first | dest_iprange_last | dest_fqdn | protocol | dest_ports | target | enabled |
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
| 187 | 3 | 56 | net | 8.8.8.8 | 32 | | | | both | 53 | ACCEPT | 1 |
| 188 | 2 | 56 | range | | 0 | 192.168.1.20 | 192.168.1.250 | | both | 53 | ACCEPT | 1 |
| 189 | 1 | 56 | fqdn | | 0 | | | my.host.com | both | 80 | ACCEPT | 1 |
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
From my controller I call Validator depending on "dest_ip_type" :
public function validationNet(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_ip', __('IP is required'))
->notEmpty('dest_ip_mask', __('Mask is required'))
->allowEmpty('dest_iprange_first')
->allowEmpty('dest_iprange_last')
->allowEmpty('dest_fqdn');
$validator
->add('dest_ip', 'notEmpty', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
return $validator;
}
public function validationIprange(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_iprange_first', __('IP is required'))
->notEmpty('dest_iprange_last', __('IP is required'))
->allowEmpty('dest_ip')
->allowEmpty('dest_ip_mask')
->allowEmpty('dest_fqdn');
$validator
->add('dest_iprange_first', 'ipv4', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
$validator
->add('dest_iprange_last', 'ipv4', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
return $validator;
}
public function validationFqdn(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_fqdn')
->allowEmpty('dest_iprange_first')
->allowEmpty('dest_iprange_last')
->allowEmpty('dest_ip')
->allowEmpty('dest_ip_mask');
$validator
->add('dest_fqdn', 'notEmpty', [
// Regex for FQDN
'rule' => ['custom', "/(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)/i"],
'message' => __('Invalid Hostname')
]);
return $validator;
}
I created 3 RulesChecker bellow to check unique differently, but How can I tell Validator or Controller to not call default "buildRules" function but force to call alternative buildRules ?
public function buildNetRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_ip', 'protocol', 'dest_ports']));
return $rules;
}
public function buildRangeRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_iprange_first', 'dest_iprange_last', 'protocol', 'dest_ports']));
return $rules;
}
public function buildFqdnRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_fqdn', 'protocol', 'dest_ports']));
return $rules;
}
I found the solution by my self. buildRules is able to check different isunique rules inside de same function. Maybe buildRules don't check isUnique rules on values that are Empty or null...
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_ip', 'protocol', 'dest_ports']));
$rules->add($rules->isUnique(['profile_id', 'dest_iprange_first', 'dest_iprange_last', 'protocol', 'dest_ports']));
$rules->add($rules->isUnique(['profile_id', 'dest_fqdn', 'protocol', 'dest_ports']));
return $rules;
}

Laravel: ORM retrieving data ordered by related table

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)

Retrieve data from database as a set of rows indexed by primary key in CodeIgniter

I have a table like this:
id | username | email
---+----------+----------------
1 | John | john#example.com
17 | Mary | mary#example.com
And I want to get a result like this:
array(
1 => array(
username => 'John',
email => 'john#example.com'
),
17 => array(
username => 'Mary',
email => 'mary#example.com'
)
);
Is it possible to do with built-in functions in CodeIgniter?
Answering my own question:
I've created a helper:
function assoc_by($key, $array) {
$new = array();
foreach ($array as $v) {
if (!array_key_exists($v[$key], $new))
$new[$v[$key]] = $v;
}
return $new;
}
Which can be used like this:
$rows = assoc_by('id', $this->db->get_where(...)->result_array());
to the best of my knowledge there no built in functions for the same, though you can create a base model, extend it and create a function for the same,
<?php
//Assuming $dbdata is the data returned as an array from database
$result = array();
if(!empty($dbdata))
{
foreach($dbdata as $key=>$value)
{
$id = $value['id'];
$result[$id] = array( 'username' => $value['username'],
'email'=>$value['email'];
);
}
return $result;
}
?>

Resources