Cannot sort collection by relationship field - laravel

I have a table called posts that stores all the post types, each post can have multiple and dynamic meta values, so I have the post_meta table which handle all of this.
The post_meta table have the following structure:
id | post_id | meta_key | meta_value
This is the structure of the posts table:
id | content | type | created_at | updated_at | deleted_at
I need to order the post with the type section, so what I did so far is this:
$sections = Post::with('meta')->where([
'type' => 'section',
'language' => 'it',
'status' => 'published',
])->get();
$sections->sortBy(function ($sec) {
return $sec->getMeta('order')->meta_value;
});
where getMeta is a custom method that I have added within the Post model:
public function getMeta(string $metaKey)
{
$key = array_search($metaKey, array_column($this->meta->toArray(), 'meta_key'));
return $key !== false ? $this->meta[$key] : null;
}
The issue is that I doesn't get any ordering, what I'm doing wrong?
Current datasets:
id | type
1 section
2 section
3 section
id | post_id | meta_key | meta_value
1 1 order 0
2 2 order 2
3 3 order 1
I should get this sequence: 1, 3, 2 instead of 1, 2, 3

When you call sortBy on collection you don't modify the existing collection,
so you need to do:
$sections = $sections->sortBy(function ($sec) {

$sections = Post::with('meta')->where([
'type' => 'section',
'language' => 'it',
'status' => 'published',
])->get();
That Where clousure is not well executed, you need 3 parameteres per array to consult, like this:
$sections = Post::with('meta')->where([
['type' '=>' 'section'],
['language' '=>' 'it'],
['status' '=>' 'published'])->get();
And i'm not pretty sure what's the query builder you are trying to build because to me it looks like more like an assignation with those arrows.......whatever but at least you have the correct format now.

Related

Laravel | three fields combination Valdiation

How to do a 3 fields combination validation?
I have this table merchant_product, the table has 4 columns :
id
merchant_id
product_id
branch_id
I don't want the same row duplicated like this when storing data.
Example :
id
merhcant_id
product_id
branch_id
1
1
1
1
2
1
1
1
I tried this approach but it seems not to work.
Here is my sample validation method :
$rules = [
'product_id' => ['required','unique:merchant_product,product_id,NULL,id,merchant_id,'.Auth::id()', new Varchar],
'merchant_id' => [new Varchar],
'branch_id' => ['required','unique:merchant_product,branch_id,NULL,id,merchant_id,'.Auth::id()', new Varchar],
];
You can use Rule::unique to validate both column together :
'product_id' => [
'required',
Rule::unique('merchant_product')->where(function ($query) use($request) {
return $query->where('product_id', $request->product_id)
->where('branch_id', $request->branch_id);
}),
],
Don't forget to add use Illuminate\Validation\Rule; on the top
Use firstOrCreate()
MerchantProduct::firstOrCreate([
'product_id' => $productId,
'merchant_id' => $merchantId,
'branch_id' => $brandId
]);
This will stop any duplicate record being created, using all 3 columns without doing extra processing with rules.
See the following article for usage:
https://laravel.com/docs/8.x/eloquent#retrieving-or-creating-models

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.

Laravel 5.4: concat data from related table in pluck

I am writing an assignment system for radio newscasts. The data tables:
station
id | calls
---| -----
1 | cxri
newscast
id | name_input | for_station_id
---|------------|---------------
1 | am | 1
assignment
id | newscast_id | user_id
1 | 1 | 5
The controller for Assignment.edit is as follows:
if (! Gate::allows('assignment_edit'))
return abort(401);
}
$relations = [
'anchors' => \App\User::get()->pluck('name', 'id')->prepend('Please select', ''),
'casts' => \App\Newscast::get()->pluck('name_input', 'id')->prepend('Please select', ''),
];
return view('assignment.create', $relations);
The Newscasts model:
public function for_station()
{
return $this->belongsTo(Station::class, 'for_station_id')->withTrashed();
}
Right now I get
"casts" => Collection {#451 ▼
#items: array:2 [▼
"" => "Please select"
1 => "am"
]
}
I want
"casts" => Collection {#451 ▼
#items: array:2 [▼
"" => "Please select"
1 => "cxri-am"
]
}
How do I make that happen?
Or should I denormalize the data and make name_input 'cxri-am' instead of 'am'?
What I've tried:
Accepted answer in Laravel pluck fields from relations
'casts' => \App\Newscast::with('station')->get()->pluck('name_input', 'id'),
errors with
Call to undefined relationship [station] on model [App\Newscast]
I included the Newscast model above based on the discussion in Laravel: get/show data from related tables. Based on that discussion it appears to me the newscast should already be able to access the Station calls. But as shown above, the calls are not in $casts.
You are close with what you have tried. The with() function needs the name of the relation function, not the table, so this should work for getting the relation model loaded:
'casts' => \App\Newscast::with('for_station')->get(),
As for getting the data to concat the way you want, I would make an accessor on the Newscast model that will return the data you want:
function getNameInputStationAttribute() {
return $this->for_station->calls . "-" . $this->input_name;
}
You would then use this like:
'casts' => \App\Newscast::with('for_station')->get()->pluck('name_input_station', 'id')->prepend('Please select', ''),
If I could point out a few other things, the return code of 401 indicates Unauthenticated whereas 403 indicates Unauthorized which would be more applicable to the Gate returning false. Also, class functions should be cammel case, so your for_station function should be forStation or even just station.

yii CGridView initial sort

Is there any way to set an initial sorting a CGridView?
I have a DB table that contains: id | name | description | create_time...
In my CGridView I want my data to be shown without the id (this can be done by setting columns in CGridView but I would like to know if there is any other method to eliminate this fields from dataProvider) and I want to have my data sorted after name ASC. How can I do this because it always sort after id and I can't set a criteria for CGridView?
You can define a default order in your model respectively the CActiveDataProvider. The following snippet sorts by the column SortMeColumn by default.
public function search()
{
$criteria = new CDbCriteria;
$criteria->compare('ID', $this->ID, true);
$criteria->compare('SortMeColumn', $this->SortMeColumn, true);
return new CActiveDataProvider($this,
array(
'criteria' => $criteria,
'sort' => array(
'defaultOrder' => array(
'SortMeColumn' => CSort::SORT_DESC
),
),
));
}
See CSort for reference.

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