Laravel relationships hasMany updateOrCreate array - php

I have User model which has relationships hasMany with UserPosition eloquent model:
User model
public function positions()
{
return $this->hasMany(UserPosition::class);
}
How I can use updateOrCreate method when from request come array of data positions?
Code
$positions = [
"doctor",
"developer"
];
$user->positions()->each(function($position) use ($positions, $user) {
$id = $user->id;
foreach ($positions as $name) {
$position->updateOrCreate(['user_id' => $id, 'name' => $name], [
'name' => $name
]);
}
});
Note: In this example user doesn't has any positions on the table of database
But my code not work. Why?

You are iterating on the existing positions of a user, meaning that if a user has no positions the iteration will never happen. You can iterate along the positions you need to make:
$positions = collect([
"doctor",
"developer"
]);
$positions->each(function($position) use ($user) {
$user->positions()->updateOrCreate(['name' => $position], [
'name' => $position
]);
}
});

It doesn't work because your running your updateOrCreate() method inside an each iteration which never runs because as you stated in your note "this example user doesn't has any positions on the table of database"
Here your trying to loop through your currently existing positions attached to your user model:
$user->positions()->each()
But that won't run because the user doesn't have any positions at the moment.
I'm guessing you are trying to update the the user's positions, where it is possible that the user already has one or more of those positions associated to him and you don't want to create duplicates, for that you can do this:
$positions = [
"doctor",
"developer"
];
foreach($positions as $position) {
UserPosition::firstOrCreate(['user_id' => $user->id, 'name' => $position]);
}

Related

Get data from array depend on database integer

I have a model of Posts with status column. I wonder, what would be be practice in Laravel to retrieve from something like 0 or 1(stored integers in DB) and instead of that show "displayed" or "hidden"? Not inside of the blade temples but when doing something like:
return response()->json(['posts' => $posts])
"status" of $posts would be not "0" but "displayed"?
You can make it in Posts Model:
const displayed = 1;
const hidden = 0;
public static function status()
{
return [
self::displayed => 'displayed',
self::hidden => 'hidden',
];
}
And retrieve it.
$post = [
"status" => Posts::status()[$request->status]
];

How to Combine Some Relations in Laravel

On my project, Picture model has some "one to many" relations to create featured images. Those relations:
public function featuredByPosts()
{
return $this->hasMany('App\Post', 'featured_image_id');
}
public function featuredByProduct()
{
return $this->hasMany('App\Products', 'featured_image_id');
}
public function featuredByPages()
{
return $this->hasMany('App\Page', 'featured_image_id');
}
One of the inverse relations is like so:
public function featured_image()
{
return $this->belongsTo('App\Picture', 'featured_image_id');
}
When I get the pictures with those relations, each picture in collection has featured_by_posts, featured_by_products and featured_by_pages keys and related content as values of those keys.
What I want to do is to create a new key named "featured_by" directly in each picture in the collection and move current featured_by_* relations into new key by modifing their keys like so:
From this:
$picture = [
"id" => "1",
"name" => "someName",
"featured_by_posts" => [array of posts],
"featured_by_pages" => [array of pages]
]
To this:
$picture= [
"id" => "1",
"name" => "someName",
"featured_by" => [
"posts" => (values of featured_by_posts),
"pages" => (values of featured_by_pages)
]
]
I don't know if it can be done while getting data from database. That's why I tried to add the codes down below in index function on my API controller to produce formatted item of picture.
$relations = ["tags", "posts", "products", "featuredByPosts", "featuredByProducts", "featuredByPages"];
$pictures = Picture::with($relations)->get();
foreach ($pictures as $picture) {
$featureds = ["posts", "products", "pages"];
$key = "featured_by";
$picture[$key] = [];
foreach ($featureds as $featured) {
$oldKey = "{$key}_{$featured}";
$picture[$key][$featured] = $picture[$oldKey]; //This line produces the error
unset($picture[$oldKey]);
}
}
//ERROR: Indirect modification of overloaded element of App\Picture has no effect.
I don't understand what that means since the think. I searched this error and found some answers, but I couldn't make it work. So I hope someone can help. Thanks.
You should use the eloquent API resources:
https://laravel.com/docs/7.x/eloquent-resources
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PictureResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'featured_by' => [
'posts' => $this->featuredByPost,
'products' => $this->featuredByProduct,
],
];
}
}
Your controller:
return new PictureResource(Picture::find(1));

Trying to return pivot data in Resource Laravel

I am trying to return pivot data to a resource.
The pivot table works, I can add and remove entrys like expected, but I am not able to get the user_id returned in ActivityResource...
In the Laravel Documentation it looks so easy, am I missing something?
// Activity.php
class Activity extends Model
{
public function members()
{
return $this->belongsToMany('App\User', 'activity_user', 'user_id', 'activity_id')->withPivot('activity_id','user_id')->withTimestamps();
}
}
// User.php
class User extends Authenticatable
{
public function joinedactivities()
{
return $this->belongsToMany('App\Activity');
}
}
In my ActivityController I want to return a newly created ActivityResource with 'eager-loaded' relationship
// ActivityController
public function show($id)
{
$activity = Activity::with('members')->findOrFail($id);
/* foreach ($activity->members as $user) {
echo $user->id . " "; // With this I can actually see the right ids
}
return;*/
return new ActivityResource($activity);
}
ActivityResource:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'attendees' => $this->whenPivotLoaded('activity_user', function () {
return $this->pivot->user_id;
}),
];
}
I dont get any errors instead the attendees field is just not returned. I tried so many things, struggeling with that. Help very appreciated.
I am using Laravel 6.
->withPivot('activity_id','user_id') is not needed. Those fields will appear on your relation object no matter what. For the resource, I think you can do the following:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
// If the relation 'members' is loaded, get an array of user ids otherwise, return null
'attendees' => $this->relationLoaded('members') ? $this->members->pluck('pivot.user_id')->unique()->all() : null
];
}
The main problem is the relationship is a Many to Many, meaning there's more than 1 pivot. With this solution, your object will look like this.
{
id: 3,
title: 'A Title',
attendees: [
1,
2,
3,
],
}
If you want the ids concatenated in a single string like in your commented foreach, replace all() by join(' ')
// If the relation 'members' is loaded, get an string of user ids otherwise, return null
'attendees' => $this->relationLoaded('members') ? $this->members->pluck('pivot.user_id')->unique()->join(' ') : null
{
id: 3,
title: 'A Title',
attendees: '1 2 3',
}

Eloquent automatically selects unwanted columns upon eager-loading model relationship

I am converting an internal API from HTML (back-end) processing to JSON (using Knockout.js) processing on the client-side to load a bunch of entities (vehicles, in my case).
The thing is our database stores sensitive information that cannot be revelead in the API since someone could simply reverse engineer the request and gather them.
Therefore I am trying to select specifically for every relationship eager-load the columns I wish to publish in the API, however I am having issues at loading a model relationship because it seems like Eloquent automatically loads every column of the parent model whenever a relationship model is eager loaded.
Sounds like a mindfuck, I am aware, so I'll try to be more comprehensive.
Our database stores many Contract, and each of them has assigned a Vehicle.
A Contract has assigned an User.
A Vehicle has assigned many Photo.
So here's the current code structure:
class Contract
{
public function user()
{
return $this->belongsTo('User');
}
public function vehicle()
{
return $this->belongsTo('Vehicle');
}
}
class Vehicle
{
public function photos()
{
return $this->hasMany('Photo', 'vehicle_id');
}
}
class Photo
{
[...]
}
Since I need to eager load every single relationship listed above and for each relationship a specific amount of columns, I need to do the following:
[...]
$query = Contract::join('vehicles as vehicle', 'vehicle.id', '=', 'contract.vehicle_id')->select([
'contract.id',
'contract.price_current',
'contract.vehicle_id',
'contract.user_id',
'contract.office_id'
]);
[...]
$query = $query->with(['vehicle' => function ($query) {
$query->select([
'id',
'trademark',
'model',
'registration',
'fuel',
'kilometers',
'horsepower',
'cc',
'owners_amount',
'date_last_revision',
'date_bollo_expiration',
'bollo_price',
'kilometers_last_tagliando'
]);
}]);
$query = $query->with(['vehicle.photos' => function ($query) {
$query->select([
'id',
'vehicle_id',
'order',
'paths'
])->where('order', '<=', 0);
}]);
$query = $query->with(['user' => function ($query) {
$query->select([
'id',
'firstname',
'lastname',
'phone'
]);
}]);
$query = $query->with(['office' => function ($query) {
$query->select([
'id',
'name'
]);
}]);
[...]
return $this->response->json([
'error' => false,
'vehicles' => $vehicles->getItems(),
'pagination' => [
'currentPage' => (integer) $vehicles->getCurrentPage(),
'lastPage' => (integer) $vehicles->getLastPage(),
'perPage' => (integer) $vehicles->getPerPage(),
'total' => (integer) $vehicles->getTotal(),
'from' => (integer) $vehicles->getFrom(),
'to' => (integer) $vehicles->getTo(),
'count' => (integer) $vehicles->count()
],
'banner' => rand(0, 2),
'filters' => (count($input) > 4),
'filtersHelpText' => generateSearchString($input)
]);
The issue is: if I do not eager load vehicle.photos relationship, columns are loaded properly. Otherwise, every single column of Vehicle's model is loaded.
Here's some pictures so you can understand:
Note: some information have been removed from the pictures since they are sensitive information.
You can set a hidden property on your models which is an array of column names you want to hide from being output.
protected $hidden = ['password'];

Yii2 Gridview current row values

I am new to Yii2. I have a gridview where 2 columns are auto-generated (not in model class). Every row has a button which when clicked, I want to access the current row cells values and do some calculation.
I am unable to use $data as it only refers to current model row. below is my code:
<?php
echo GridView::widget([
'dataProvider' => $model->getInsuredCompanyVehiclesArrayDataProvider(),
'filterModel' => $model->getInsuredCompanyVehiclesArrayDataProvider(),
'columns' => [
[
'label' => 'Amount',
'format' => 'raw',
'value' => function ($data) {
return $data->rate * Yii::$app->session->get('motorInsuranceEntry')->sumInsured/100;
},
],
[
'label'=>'Total',
'format' => 'raw',
'value' => function ($model, $key, $index, $column) {
//return $data->rate * Yii::$app->session->get('motorInsuranceEntry')->sumInsured/100 + $model->amount;
return '' . $column ;
},
],
],
]); ?>
The amount and Total colum values are dynamically calculated but I am unable to access these values in other cells :(
As you might already know due to that the columns are dynamically you couldn't access them from other columns.
I would create functions for both dynamically columns (Amount and Total) in the model class to calculate your dynamically columns. And after you click your button in the row refresh the grid via pajax.
You could also add additional data to the gridview like metioned here
by either extend your gridview model or use inline functions (req. PHP 5.3+).
But then you also have to calculate each column value again if they are completely dynamically by each row.
But i guess your simple mistake is that you use one time
'value' => function ($data) {
and the other time
'value' => function ($model, $key, $index, $column) {
Both functions are correct. One time you pass the model to the function by the parameter "name" $data and the other time with the "name" $model.
Inside the function you only have access to the parameters which are passed to the function.
Your second inline function code should use $model instead of $data (first parameter name is the model).
return $model->rate * Yii::$app->session->get('motorInsuranceEntry')->sumInsured/100 + $model->amount;
But as i mentioned i would create a function in your Model class
public function getAmount() {
return $this->rate * Yii::$app->session->get('motorInsuranceEntry')->sumInsured/100;
}
What i don't understand is that your Total is just the amount * 2 ?
But i guess this was just some testing code.

Categories