Laravel Pluck and combine two fields to generate a select menu - php

Let's say I have the following fields in my database for a user, id, first_name and last_name. I want to generate a select menu, something like below:
<select>
<option value="id">first_name + last_name</option>
</select>
I know I can use pluck to get an array with id as the key and one more field as the value:
User::pluck('first_name', 'id')->all();
And this would produce:
array:4 [▼
1 => "John"
2 => "Linda"
3 => "Jane"
4 => "Carl"
]
Is there an easy way using eloquent to get the first_name AND the last_name so I can get the following:
array:4 [▼
1 => "John Doe"
2 => "Linda Smith"
3 => "Jane Doe"
4 => "Carl Johnson"
]
The first_name and last_name are examples to explain my question, I need to combine two other fields instead which cannot be 'merged' into one field.

Best way to do this is to append the model attribute on fly get full name.
In Model
class User extends Model
{
public $appends = [ 'full_name' ];
public function getFullNameAttribute()
{
return $this->first_name .' '. $this->last_name;
}
}
In Controller
User::pluck('full_name', 'id')->all();
If you are using Laravel latest version, follow this as well https://laravel.com/docs/9.x/eloquent-serialization#appending-values-to-json

Related

Best way to transform tag list into structured array from MySQL?

My database is storing a list like this:
ID | name
--- -----
1 | search
2 | search.categories
3 | search.categories.accessories
4 | search.categories.accessories.glasses
5 | search.categories.accessories.hats
6 | filters
7 | filters.payments
8 | filters.payments.creditcard
9 | filters.payments.debitcard
Now I want to SELECT it in a recursivelly array just like this:
[
name, childs[
name, childs[ ... ],
name, childs[ ... ],
name, childs[
name, childs[ ... ],
name, childs[ ... ],
],
],
name, childs[
name, childs[ ... ],
name, childs[ ... ],
name, childs[
name, childs[ ... ],
name, childs[ ... ],
],
]
]
What is the best way to achive this? Directly from MySQL or manually string handling?
If you are working with Laravel you can build a nested array by using array_set and using as key the name column of your table, taking advantage that you are already storing on dot notation.
Despite you are not exposing what's the purpose of this and why you want to build this structure this way, follows one possible approach.
Initialize an empty array ($structure = [])
Retrieve all table records an iterate them (use a DB select or model get as you prefer)
For each table row, perform an array_set($structure, $listname, $value)
In your example, I don't see where $value could be coming from, but I guess you will grasp the idea.
$structure = [];
foreach($rows as $row)
{
array_set($structure, $row->name, $value);
}

Get count of unique values with the column name in Laravel 5.2 Collection

I am trying to get the number of unique Brands from my Products Table with their count from a Laravel collection.
I was able to do that using a specific query for the products but the reason i'm using a collection now is because i also want to get the Product Origins (Country), Conditions (Used / New) of the products and i thought it would be much better to use a collection from one query rather than have three separate queries for each data.
The code below works but it doesn't show the count for each unique Brand.
Here is the Table
Here is my Controller
$products = DB::table('products')
->select('products.*')
->whereNull('products.deleted_at')
->get();
$BrandCollection = collect($products);
$Brands = $BrandCollection->unique('Brand')->sortBy('Brand')->keyBy('Brand')->pluck('Brand');
So, the result i'm looking for is
HP 3
Toshiba 2
Lenovo 1
I thought it could be done using concat for the collection but since i'm on Laravel 5.2, i'm looking for other solutions.
If you really want to use collections (not Eloquent) you can do it like this:
$brandsWithCount = $BrandCollection->groupBy('Brand')->map(function($values) {
return $values->count();
})->sort()->reverse();
For example if you set $brandCollection like this:
$BrandCollection = collect([
['Brand' => 'HP'],
['Brand' => 'HP'],
['Brand' => 'HP'],
['Brand' => 'Toshiba'],
['Brand' => 'Toshiba'],
['Brand' => 'Lenovo'],
]);
result will be:
Collection {#372
#items: array:3 [
"HP" => 3
"Toshiba" => 2
"Lenovo" => 1
]
}
as expected.
There is a Collection Helper called CountBy, does exactly what you need.
Collections CountBy
$BrandCollection->countBy('Brand');
It will retourn as expected
#items: array:3 [
"HP" => 3
"Toshiba" => 2
"Lenovo" => 1
]
Simple :D

Laravel 5.1 loading relationship data into selected row

I have a timesheet table and a user table in my database. The following relationship is setup on the timesheet model.
/**
* The user that owns the timesheet.
*
* #return Object
*/
public function user()
{
return $this->belongsTo('App\Models\User\User');
}
The above means I can get user data when I select timesheets from the database by using something like:
$this->timesheet->whereStatus('Approved')->with('user');
This will load the user object in the result and if converted to an array will look like so;
0 => array:13 [▼
"id" => 1
"user_id" => 2
"week" => 1
"year" => 2016
"week_ending" => "Sunday 10th January 2016"
"total_hours" => "45.00"
"token" => "0e6796a2dc68066c8d36ff828c519af00657db02b733309b8a4ac0f7b5d6a385"
"status" => "Approved"
"supervisor_id" => 1
"approved_by" => 1
"created_at" => "2016-01-13 15:42:49"
"updated_at" => "2016-01-14 14:52:07"
"user" => array:7 [▼
"id" => 2
"first_name" => "Bill"
"last_name" => "Andrews"
"email" => "Bill.andrews#domain.com"
"status" => 1
"created_at" => "2016-01-13 15:38:18"
"updated_at" => "2016-01-14 14:50:03"
]
]
However, I only need first_name and last_name from the user table. Is there a way to merge/flatten the user array with the timesheet so that it looks like this instead;
0 => array:14 [▼
"id" => 1
"user_id" => 2
"week" => 1
"year" => 2016
"week_ending" => "Sunday 10th January 2016"
"total_hours" => "45.00"
"token" => "0e6796a2dc68066c8d36ff828c519af00657db02b733309b8a4ac0f7b5d6a385"
"status" => "Approved"
"supervisor_id" => 1
"approved_by" => 1
"created_at" => "2016-01-13 15:42:49"
"updated_at" => "2016-01-14 14:52:07"
"first_name" => "Bill"
"last_name" => "Andrews"
]
I have tried to use eager loading like so;
$this->timesheet->with(['user' => function ($query) {
$query->select('first_name', 'last_name');
}])->get()->toArray();
However, it results in the following output;
array:126 [▼
0 => array:13 [▼
"id" => 1
"user_id" => 2
"week" => 1
"year" => 2016
"week_ending" => "Sunday 10th January 2016"
"total_hours" => "45.00"
"token" => "0e6796a2dc68066c8d36ff828c519af00657db02b733309b8a4ac0f7b5d6a385"
"status" => "Approved"
"supervisor_id" => 1
"approved_by" => 1
"created_at" => "2016-01-13 15:42:49"
"updated_at" => "2016-01-14 14:52:07"
"user" => null
]
The reason why the user relationship is null in your second example is because in order for Eloquent relationships to work, it needs the keys that tie the relationships. In other words...
Get timesheet.
Get user with only first_name and last_name.
Build relationship.
Since you did not fetch the user's id, the user's id and the timesheet's user_id do not match so the relationship cannot be built.
In order for your query to work, you need to adjust it like this:
$this->timesheet->with(['user' => function ($query) {
$query->select('id', 'first_name', 'last_name');
}])->get()->toArray();
With that out of the way, if you want a flattened result, I think it's best to use joins rather than eager loading because of the nature of eager loading.
$this->timesheet
->join('users', 'timesheets.user_id', '=', 'users.id')
->select('timesheets.*', 'users.first_name', 'users.last_name')
->get()->toArray();
This assumes that your table names are users and timesheets.
Have you tried to get a list
->lists('first_name', 'last_name');
or if you wanted to perform a select
->select('first_name', 'last_name')->get()
Update
You can also perform eager loading to eager load related objects. Here is an example
$users = App\User::with(['posts' => function ($query) {
$query->select('first_name', 'last_name');
}])->get();
Please let me know if that helps.
Laravel models have a way to modify data before getting/setting an attribute. You can actually add the attributes to the model by defining a getter function. This will let you reference the user names in the same way you would the user_id or status. These functions are also great for changing date formats for a view or sanitizing form input.
/**
* Get the first name of the user.
*
* #return string
*/
public function getFirstNameAttribute()
{
return $this->user->first_name;
}
/**
* Get the last name of the user.
*
* #return string
*/
public function getLastNameAttribute()
{
return $this->user->last_name;
}
That's exactly what a join do.
From the documentation
The query builder may also be used to write join statements. To
perform a basic SQL "inner join", you may use the join method on a
query builder instance. The first argument passed to the join method
is the name of the table you need to join to, while the remaining
arguments specify the column constraints for the join.
$this->timesheet
->leftjoin('user', 'user.id', '=','timesheet.user_id')
->get()
->toArray();
If you want to be more selective on your fields, you can choose what you select;
$this->timesheet
->leftjoin('user', 'user.id', '=','timesheet.user_id')
->select('timesheet.*', 'user.first_name', 'user.last_name')
->get()
->toArray();
As other have suggested, it might be better to use the DB query builder such as
this->timesheet = DB::table('timesheet')
->where('timesheet.status', 'Approved')
->leftjoin('user', 'user.id', '=','timesheet.user_id')
->select('timesheet.*', 'user.first_name', 'user.last_name')
->get()
->toArray();
In this case it's better to use Raw SQL or Query builder.
Because relations made for using database data as an relational objects.

Laravel 5.2 Eloquent column name conflict

I have the following method in my user class:
/**
* Get all organisations for user (if owner)
*
* #param
*/
public function getOrganisationsOwned()
{
// If the user is owner of any one or many organisations then return this list
return Organisation::leftJoin('subscription_plans', 'organisations.subscription_plan_id', '=', 'subscription_plans.id')
->where('organisations.owner_id', '=', $this->id)
->select('organisations.*', 'subscription_plans.*')
->get();
}
The method essentially queries and joins two tables. Each table has a column called title.
The output from the above generates the rows as desired with the right info, but returns only one title column, from the right table (subscription_plans) but not the column title from the left table (organisations). I also notice it is dropping the timestamps from one table also, as these are of the same column name.
I understood that
->select('organisations.*', 'subscription_plans.*')
would make the query return both columns. What am I missing? Happy new year!
PS: below is a copy of the dd() contents for the collection, with title only appearing once.
#attributes: array:44 [▼
"id" => 1
"title" => "Monthly Subscription"
"address_1" => "34 Florence Street"
"address_2" => ""
"suburb" => "Hornsby"
"state" => "NSW"
"postcode" => "2077"
"country_id" => 12
"currency_id" => 12
"time_zone_id" => 109
"phone" => "0392144497"
"website" => "http://www.Tremblay.com/est-aspernatur-et-ut-provident.html"
"business_id" => "82297955560"
"tax_registration" => 1
"logo" => "8aa656de-2bc2-4e14-dddd-e02fbcd2b76f"
"quote_terms_days" => 14
"invoice_terms_days" => 14
"fiscal_start_id" => 7
"industry_id" => 4
"company_size_id" => 3
"date_format_id" => 2
"date_time_format_id" => 20
"owner_id" => 1
"gateway_id" => "1"
"gateway_username" => "xxx"
"gateway_password" => "xxx"
"gateway_signature" => "xxx"
"gateway_accepted_cards" => "[1, 2, 3]"
"subscription_plan_id" => 1
"trial_ends_at" => "2015-11-07"
"grace_ends_at" => "2016-02-10"
"subscription_ends_at" => "2016-01-11"
"latitude" => "-33.70433500"
"longitude" => "151.10161900"
"registration" => "done"
"deleted_at" => null
"created_at" => "2016-01-01 14:59:47"
"updated_at" => "2016-01-01 14:59:47"
"amount" => "9.09"
"gst" => "0.91"
"gst_amount" => "10.00"
"billing_cycle" => "MONTH"
"trial_period_days" => 30
"grace_period_days" => 30
]
The "missing" title column contains:
'title' => 'ABC Electrical'
There is some misunderstanding as to what I suggested: instead of using *, you could list the field names one by one and provide aliases for the 2 title fields. This does not mean that you should keep the 'organisations.*', 'subscription_plans.*' and add the 2 title fields to the select list with aliases because this way you select both title fields twice, wasting memory and processor time.
You should not include the * forms in the select list, but list each field individually, with the 2 title fields marked with aliases:
public function getOrganisationsOwned()
{
// If the user is owner of any one or many organisations then return this list
return Organisation::leftJoin('subscription_plans', 'organisations.subscription_plan_id', '=', 'subscription_plans.id')
->where('organisations.owner_id', '=', $this->id)
->select('organisations.id', 'organisations.title AS org_title', ..., 'subscription_plans.subscription_plan_id', 'subscription_plans.title AS plan_title', ...)
->get();
}
Yeah, I know, listing so many field one by one is a pain in the ***, however, each field is retrieved once and only once, at it is clear that you are fetching what is needed.
#Shadow's suggestion worked, although you should note, this method allows you to select all the fields, but only "rename" columns or rather alias them so you can still access the proper value when using joins. The old value will still be overridden, but now you can use your alias with the correct value.
The below is now working:
public function getOrganisationsOwned()
{
// If the user is owner of any one or many organisations then return this list
return Organisation::leftJoin('subscription_plans', 'organisations.subscription_plan_id', '=', 'subscription_plans.id')
->where('organisations.owner_id', '=', $this->id)
->select('organisations.*', 'organisations.title AS org_title', 'subscription_plans.*', 'subscription_plans.title AS plan_title')
->get();
}

Laravel Sync method only sending the 2nd data

This piece of code should be deleting all old data from the database and when add the new ones (using sync())
Now I have a project with users, and a user can be linked to a project with a checkbox.
So on checkbox checked this function will trigger, but for example when I say that user 1 and user 2 are going through this fuction to get added to the pivot table it will only send user 2, and user 1 will not get through, what is going wrong?
And when I add 3 users user 1, user 2, user 3, only user 2 will get added.
Controller
public function update(CreateProjectRequest $request)
{
if($request->get('contribute'))
{
foreach($request->get('contribute') as $k => $contribute)
{
if($contribute == 1)
{
$project = $this->project->find($request->project_id);
$project->users()->sync(array($k));
}
}
}
$project = $this->project->find($request->project_id);
$project->fill($request->input())->save();
return redirect('project');
}
Blade
#foreach($users as $user)
<tr>
<td>
{{$user->firstname}} {{$user->middlename}} {{$user->lastname}}
</td>
<td>
{!! Form::checkbox('contribute['.$user->id.']', '1', $user->projects->contains('id', $project->id)) !!}
</td>
</tr>
#endforeach
On a dd($request->input()); at the start of my update method (with selecting atleast 3 users) this will get in return:
array:9 [▼
"_method" => "PATCH"
"_token" => "0uIZNn6zwZjVKfgE0ckhDULeYda0OaLzKVdUgoM8"
"name" => "Dire Straits"
"completion_date" => "2015-05-18"
"DataTables_Table_0_length" => "10"
"contribute" => array:3 [▼
1 => "1"
3 => "1"
2 => "1"
]
"completed" => "1"
"active" => "0"
"project_id" => "11"
]
So 1 / 3 / 2 would be the user_id and => 1 should be the value.
The problem is that sync gets called in loop 3 times so each time it's syncing one value. You have to pass an array of the ids in sync ex:
$project->users()->sync([1,3,2]);
Or if you want you can use attach when contribute==1 and detach when contribute==0
Or if contribute doesnt return input when a user is deselected and it only returns when it is selected then you can try:
$this->project->users()->sync(array_keys($request->get('contribute'));
I just noticed that you have another bug unless you are updating many projects with one call you should put the line below on the first line of your function.
$project = $this->project->find($request->project_id);

Categories