How to add extra object in laravel relationship when fatch data.
Here is my code:
$list = new self;
$list = $list->where('uuid', $uuid);
$list = $list->where('user_id', $user->id)->orWhere('to_user_id', $user->id);
$list = $list->with(['touser' => function($q1) {
$q1 = $q1->select('id', 'name', 'email', 'user_image', 'is_active', 'is_profile_completed', 'is_verified');
}]);
$list->with(['user' => function($q1) {
$q1 = $q1->select('id', 'name', 'email', 'user_image', 'is_active', 'is_profile_completed', 'is_verified');
}]);
$list = $list->first();
I want to add extra object in response like:
"Contest": {
"name": NULL,
"type": 0
}
You have multiple ways to add extra objects in response
The first option is
You have to define the eloquent relationship in the list model like this
public function contest()
{
return $this->belongsTo(Contest::class);
}
and then you can eager load that relationship using a method like this
$list->with('contest')->first();
another option is
You can set custom relationships like this
$list->setRelation('contest', collect(['name'=>null,'type':0]));
try this:
$list->prepend([
'Contest' => [
'name' => null,
'type' => 0
]
]);
Related
How I can add custom options in a belongsTo, I found another solution, but I think isn't correct/beauty
public function fields(Request $request)
{
$rooms = array();
$resourceId = ($request->viaResourceId) ? $request->viaResourceId : $request->resourceId;
$event = \App\Event::where('id', $resourceId)->first();
$rooms = array();
foreach ($event->hotels()->get() as $hotel) {
foreach ($hotel->roomTypes()->get() as $room) {
$rooms[$room->id] = $hotel->name.' - '.$room->title;
}
}
return [
(new Tabs('Configuración', [
'Tarifa' => [
BelongsTo::make('Evento', 'event', Event::class)->required(),
Select::make('Habitación', 'room_type_id')->options($rooms)->displayUsingLabels(),
BelongsTo::make('Régimen', 'board', Board::class)->required(),
Money::make('Importe', 'EUR', 'amount')->required(),
],
'Fechas' => [
Date::make('Fecha desde', 'checkin')->required()->creationRules(REQUIRED, 'after_or_equal:today'),
Date::make('Fecha desde', 'checkout')->required()->rules(REQUIRED, 'after_or_equal:checkin'),
],
'Cupo' => [
Number::make('Ocupación', 'quota')->required()->hideFromIndex(),
Number::make('Reservados', 'booked')->readonly()->hideFromIndex(),
],
'Info Adicional' => $this->detailDataPanel(),
]))->withToolbar()
];
}
I want
Select::make('Habitación', 'room_type_id')->options($rooms)->displayUsingLabels(),
convert to
BelongsTo::make('Habitación', 'room_type_id')->options($rooms)->displayUsingLabels(),
I tried with relatables, but Room Types depend of the Hotels related with Event.
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));
Is there a way to use updateOrCreate() or similar method for polymorphic relationship?
Problem with using updateOrCreate method I wouldn't know the id in the polymorphic table.
Currently doing like this:
if ($request->has('meta.description')) {
$description = $cat->tags()->where('key', 'description')->first();
if (is_null($description)) {
$cat->tags()->create([
'client_id' => client()->id,
'key' => 'description',
'value' => $request->input('meta.description'),
]);
} else {
$description->update([
'value' => $request->input('meta.description'),
]);
}
}
Tag method like like this Cat model:
public function tags()
{
return $this->morphMany('App\Tag', 'taggable', 'table_name', 'table_id');
}
You need to pass in a second parameter to updateOrCreate. Try splitting the array up into two like so:
$cat->tags()->updateOrCreate(
[
'client_id' => client()->id,
'key' => 'description'
],
[
'value' => $request->input('meta.description')
]
);
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'];
I'm making a multi select form element for updating schools and specialties pivot table school_specialty. The problem is that when I change only something in multi select not other inputs or textareas, I can't listen model events so I can't sync school_specialty table. But when I fill in any other input it's works perfect. Here's my multi select from blade:
{{Form::select('specialties[]', $specialties_data, $school->specialties, array('multiple' => 'true', 'id' => 'multi-select'))}}
This is my update method from school controller:
public function update($id)
{
$data = Input::only('name', 'type_id', 'description', 'info_specialties', 'contacts', 'specialties', 'financing_id', 'district_id', 'city_id');
$school = School::find($id);
$school->name = $data['name'];
$school->type_id = $data['type_id'];
$school->description = $data['description'];
$school->info_specialties = $data['info_specialties'];
$school->contacts = $data['contacts'];
$school->cover_photo = Input::file('cover_photo');
$school->set_specialties = $data['specialties'];
$school->financing_id = $data['financing_id'];
$school->set_district_id = $data['district_id'];
$school->city_id = $data['city_id'];
try {
$school->save();
} catch (ValidationException $errors) {
return Redirect::route('admin.schools.edit', array($id))
->withErrors($errors->getErrors())
->withInput();
}
return Redirect::route('admin.schools.edit', array($id))
->withErrors(array('mainSuccess' => 'School was created.'));
}
And here's my example school model:
<?php
class School extends Eloquent {
protected $table = 'schools';
protected $fillable = array('name', 'type_id', 'description', 'city');
protected $guarded = array('id');
protected $appends = array('specialties');
public $set_specialties;
public $set_district_id;
protected static function boot()
{
parent::boot();
static::updating(function($model)
{
$data = array(
'name' => $model->name,
'type_id' => $model->type_id,
'description' => $model->description,
'specialties' => $model->set_specialties,
'city_id' => $model->city_id
);
$rules = array(
'name' => 'required|min:3|max:50',
'type_id' => 'required|min:1|max:300000',
'description' => 'required|min:10',
'specialties' => 'required|array',
'city_id' => 'required|min:1|max:300000'
);
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new ValidationException(null, null, null, $validator->messages());
} else {
return true;
}
});
static::updated(function($model)
{
if ( $model->set_specialties != null )
{
$model->specialty()->sync($model->set_specialties);
}
});
}
public function specialty()
{
return $this->belongsToMany('Specialty', 'school_specialty');
}
}
?>
When updating only school specialities the School model events aren't triggered because the School model stays the same.
I think the simplest and most elegant solution is to touch the school model instance. This will modify the updated_at field for the School object and thus trigger the model events.
To do this add the following lines before the try/catch block:
if ($school->set_specialties !== null) {
$school->touch();
}
Also, validation shouldn't be handled in the model observers. Check form request validation here: https://laravel.com/docs/5.6/validation#form-request-validation.