Laravel Sync method only sending the 2nd data - php

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

Related

Laravel Pluck and combine two fields to generate a select menu

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

Problem with inserting and updating main video in database in Laravel project

I am working on a project for ads/properties in Laravel. I have gallery of multiple videos for each individual ad. I want to be able to select one of those videos with radio button and make it primary video for that ad (show that particular video next to ad). I have has many relationship between property and video and I am trying to insert id of video from videos table to main_video_id column in properties table and also when I change current video for new one to update that foreign key accordingly. I am having trouble to write that method in my controller. Any help is appreciated. Here are my tables and code.
properties (id, title, location, price, main_video_id)
videos (id, filename_video, model_id)
In videos table model_id column is foreign key that connects to properties table.
PropertyController.php
public function edit(Property $property, Request $request)
{
if (!($property->user_id == Auth::user()->id)) {
abort(404);
}
$category = $property->category;
foreach ($property->photos as $value) {
$photoarray[] = $value->filename;
};
empty($photoarray) ? $photo = '' : $photo = implode(",", $photoarray);
foreach ($property->videos as $value1) {
$videoArray[] = $value1->filename_video;
};
empty($videoArray) ? $video = '' : $video = implode(",", $videoArray);
$data = .... Here I am having trouble writing method!
return view('property.edit', compact(
'category',
'photo',
'property',
'video',
'data'
));
}
edit.blade.php
<form id="form" action="/property/{{$property->id}}" method="POST" enctype="multipart/form-data">
<input type="hidden" id="hideninput" data-src="property/{{$property->id}}/gallery"value="{{old('video',$video)}}" name="video">
#if (old('video', $video))
#foreach (array_filter(explode(',',old('video', $video)), 'strlen') as $key =>$value)
<div id="{{'div_video_'.$key}}" class="col-md-3" style="padding: 15px;">
#foreach ($data as $key => $value)
<input type="radio" name="radio" value="{{ $value->video_id }}">Make main
#endforeach
<button data="{{$key}}" type="button" class="closebuttonvideo">
<span aria-hidden="true">×</span>
</button>
<video id="{{'video_'.$key}}" data={{$value}} src="/storage/property/{{$property->id}}/gallery/{{$value}}" class="video-js vjs-default-skin" controls preload="auto" data-setup='{"inactivityTimeout": 0}' width="180" height="180">
</video>
</div>
#endforeach
#endif
Property.php
public function videos()
{
return $this->hasMany(Video::class, 'model_id');
}
Video.php
public function properties()
{
return $this->belongsTo(Property::class);
}
Instead of having main_video_id in the properties table, I would go with the following approach:
Add a column is_main_video to videos table and a composite unique constraint as below:
$table->boolean('is_main_video')->nullable()->default(null);
$table->unique(['model_id', 'is_main_video']);
The unique constraint ensures that there are no multiple main videos for a single property.
In Property.php
protected $appends = ['mainVideo'];
public function videos()
{
return $this->hasMany(Video::class, 'model_id');
}
public function getMainVideoAttribute()
{
return $this->videos->first(function($video) {
return $video->is_main_vide;
});
}
Now when you fetch properties, you'll automatically get the mainVideo and related videos as a result as show below:
array:4 [
"id" => 1
"name" => "property 1"
"mainVideo" => array:4 [
"id" => 2
"model_id" => 1
"is_main_video" => 1
"name" => "Video 2"
]
"videos" => array:2 [
0 => array:4 [
"id" => 1
"model_id" => 1
"is_main_video" => null
"name" => "Video 1"
]
1 => array:4 [
"id" => 2
"model_id" => 1
"is_main_video" => 1
"name" => "Video 2"
]
]
]
Hope it helps!

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

Categories