How to get a collection of multiple ralationships using Eloquent? - laravel

I have a table that contains foreign keys of product properties and their values.
product_id
feature_id
value_id
1
1
2
1
1
3
1
2
4
How to get a collection of that values using Eloquent?
Something like this
product => [
id => 1,
...
features => Collection: [
Feature: [
id => 1,
name => Size,
values => [
Value: [
id => 2,
name => M
],
Value: [
id: 3,
name: L
]
]
],
Feature: [
id => 2,
name => Color,
values => [
Value: [
id => 4,
name => Red
]
]
]
]
]
This is my solution.
class Product extends Model
{
protected $appends = ['features'];
protected array $features;
protected bool $featuresAppend = false;
public function getFeaturesAttribute(): Collection
{
if ($this->featuresAppend === false) {
$this->appendFeatures();
}
return collect($this->features);
}
public function productFeatures(): BelongsToMany
{
return $this->belongsToMany(Feature::class, 'product_features');
}
public function productFeatureValues(): BelongsToMany
{
return $this->belongsToMany(
FeatureValue::class,
'product_features',
'product_id',
'value_id'
);
}
protected function appendFeatures(): void
{
// Get futures and values by relationships
$features = $this->productFeatures()->get()->keyBy('id');
$values = $this->productFeatureValues()->get()->keyBy('id');
// Create features-values structure
$features->each(
function ($f) {
$this->features[$f->id] = new \StdClass();
$this->features[$f->id]->feature = $f;
}
);
// Appends values to future
$values->each(
function ($v) {
if (array_key_exists($v->feature_id, $this->features)) {
$this->features[$v->feature_id]->values[$v->id] = $v;
}
}
);
$this->featuresAppend = true;
}
}
I'm looking for a better solution to this problem or a solution using native Laravel features.

Related

How to insert Custom row Laravel excel collection

I trying to export a Excel file, from a collection using laravel. The code bellow, returns me this. I need to add a 2 new rows above the start of columsn, is that possible? What Should I do?
<?php
namespace App\Traits;
namespace App\Exports;
// use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithColumnWidths;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
class exportSafety implements FromCollection, WithHeadings, WithMapping, WithColumnWidths, WithStyles, WithColumnFormatting, WithCustomStartCell
{
protected $services;
protected $request;
public function __construct(Collection $services)
{
$this->services = $services;
}
public function startCell(): string
{
return 'A3';
}
public function columnWidths(): array
{
return [
'A' => 30,
'B' => 20,
'C' => 20,
'D' => 15,
];
}
public function columnFormats(): array
{
return [
'B' => NumberFormat::FORMAT_DATE_DDMMYYYY,
];
}
public function styles(Worksheet $sheet)
{
return [
// Style the first row as bold text.
3 => ['font' => ['bold' => true]],
];
}
public function collection()
{
$this->services->each(function ($service) {
$this->map($service);
});
return $this->services;
}
public function headings(): array
{
$columns = [
'Name' => 'Name',
'Data de Nascimento' => 'Data de Nascimento',
'CPF' => 'CPF',
'Valor do Seguro' => "Valor do Seguro"
];
return $columns;
}
public function startRow(): int
{
return 2;
}
public function map($service): array
{
$columns = [
'Name' => $service->name,
'Data de Nascimento' => $service->birthdate,
'CPF' => $service->cpf,
'Valor do Seguro' => $service->client->donation_safety
];
return $columns;
}
}
But I need something like this:
I need to put two custom row above the start of the columns, is that possible? How I to that? I am using https://docs.laravel-excel.com/3.1/imports/multiple-sheets.html
you need to use WithHeadings trait to format heading rows.
$rangeHeadings = [
'chiqaruvchi',
'chiqarilgan',
'sotilgan',
"sana",
];
public function headings(): array
{
return [
[
$this->product?->name . " product " . $this->params['from_date'] . ' - ' . $this->params['to_date'] . " report",
],
$rangeHeadings,
];
}

Asking an advise about Pivot Table

I have 3 tables like this
Student:
id | name
class Student extends Model
{
protected $table = 'students';
public function marks () {
return $this->belongsToMany( 'App\Models\Subject', 'marks' )->using( 'App\Models\Mark');
}
}
Subject:
id | name
class Subject extends Model
{
protected $table = 'subjects';
public function marks () {
return $this->belongsToMany( 'App\Models\Student', 'marks' )->using( 'App\Models\Mark');
}
}
Mark:
subject_id | student_id | type_of_exam | times_taking_exam | mark
class Mark extends Pivot
{
protected $table = 'marks';
public function student() {
return $this->belongsTo('App\Models\Student','student_id');
}
public function subject() {
return $this->belongsTo('App\Models\Student','subject_id');
}
public $timestamps = false;
}
The table Mark which has 4 column primary key (id_subject, id_student, type_of_exam, times_taking_exam)
I don't know my design database is correct or not, but if it did, how can I use sync to update or insert mark for my student.
Because I understand that the method sync is like this:
$student->marks()->sync([
$subject_id => [
'type_of_exam' => 1,
'times_taking_exam' => 1,
'mark' => 10,
]
]);
But it is not correct for this situation cause it will run this code like this:
Mark::updateOrCreate( [
'student_id' => $student_id,
'subject_id' => $subject_id,
], [
'type_of_exam' => 1,
'times_taking_exam' => 1,
'mark' => 10
] );
Instead, I want this:
Mark::updateOrCreate( [
'student_id' => $student_id,
'subject_id' => $subject_id,
'type_of_exam' => 1,
'times_taking_exam' => 1,
], [
'mark' => 10
] );
I have tried use Mark as Model but I think it's not the best way.

Base table or view not found: 1146 Table 'pht.location_id' doesn't exist

I have no table in my database in this name (location_id) but it gives me the error.
"message": "SQLSTATE[42S02]: Base table or view not found: 1146 Table
'pht.location_id' doesn't exist (SQL: select locations.*,
location_id.location_trails_id as pivot_location_trails_id,
location_id.location_id as pivot_location_id from locations
inner join location_id on locations.id =
location_id.location_id where location_id.location_trails_id
in (11, 13, 15, 16, 121, 123, 124, 181))",
I cannot find how to resolve it. Please Help me To resolve this issue. Thanks In advance.
Here is the code of my controller.
public function get($id)
{
$locations = LocationTrails::whereTrailId($id)->with('locations')->orderBy('ordered','ASC')->get();
dd($locations);
// $locations = Trail::findOrFail($id)->locations()->paginate($this->perPage);
// dd($locations);
$tdata = [];
foreach($locations->items() as $location) {
$hints = $location->hints;
$hintsData = [];
foreach($hints as $hint) {
$hintsData[] = [
'title' => $hint->title,
'hint_text' => $hint->hint_text,
'hint_solution_text' => $hint->hint_solution_text,
'hint_image_path' => $hint->hint_image_path,
'hint_video_path' => $hint->hint_video_path,
'complexity_order' => $hint->complexity_order
];
}
$tdata[] = [
'id' => $location->id,
'title' => $location->title,
'radius' => $location->radius,
'latitude' => $location->latitude,
'longitude' => $location->longitude,
'rewards_points' => $location->rewards_points,
'location_info_link' => $location->location_info_link,
'hints' => $hintsData
];
}
$data = [];
$data['data'] = $tdata;
$data['meta']['paginator'] = $this->getPaginatorInfo($locations);
return $data;
}
Here is my model of location
class Location extends Model
{
protected $table = 'locations';
protected $fillable = [
'title', 'radius', 'latitude', 'longitude', 'rewards_points', 'location_info_link', 'location_order'
];
/********************* RELATIONS *********************/
public function trails()
{
return $this->belongsToMany(Trail::class);
}
public function hints()
{
return $this->hasMany(LocationHint::class);
}
public function location_trails()
{
return $this->belongsTo(LocationTrails::class,'location_id');
}
Here is my locationtrail model:
class LocationTrails extends Model
{
protected $table = 'location_trail';
protected $fillable = [
'location_id', 'trail_id', 'ordered', 'created_at', 'updated_at',
];
/********************* RELATIONS *********************/
public function trails()
{
return $this->belongsToMany(Trail::class,'trail_id');
}
public function locations()
{
return $this->belongsToMany(Location::class,'location_id');
}
problem is in your LocationTrails model. You define your method like $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id'); so, laravel think location_id is a table name and the error thrown.
If it is one to many relationship.
public function locations()
{
return $this->hasMany(Location::class,'location_id');
}
If it is many to many relationship then you can see this.
https://laravel.com/docs/5.8/eloquent-relationships#many-to-many
same for your trails() method.
public function trails()
{
return $this->hasMany(Trail::class,'trail_id');
}

laravel - change value in array for API Resources

{
"data": {
"username": "candy",
"certificates": [
{
"id": 11,
"category_id": 1,
"certname": "cert name test",
"created_at": "2018-08-18 00:58:12",
"updated_at": "2018-08-18 00:58:12"
}
]
}
}
Above is a response by using Eloquent: API Resources.
I would like to put category name instead of category_id.
Below is resource class
public function toArray($request)
{
return [
'nickname' => $this->nickname,
'certificates' => $this->certificates,
];
}
certificates are in array (hasMany relationship)
certificates belongsTo category
In this case, in the resource class you can fetch category name for each certificate and attach it.
public function toArray($request)
{
$certificateList = $this->certificates;
foreach($certificateList as $ceritificate) {
// fetch only the category name from the database.
// attach to the certificate.
$categoryName = Category::where('id', $certificate->category_id)->pluck('name')->first();
$certificate->setAttribute('category_name, $categoryName);
}
// then as usual
return [
'nick_name' => $this->nick_name,
'certificates' => $certificateList
];
}
This Might Help,
Run a foreach loop
$category = "Your SELECT Query";
foreach ($category as $value) {
$categoryId = json_decode($value->category_id);
$x = Category::select('certname')->whereIn('id', $categoryId)->get();
foreach ($x as $as) {
$y[] = $as->certname;
}
$value->certname = $y;
you can just do it like this
in controler
return CertificateResource::collection(Certificate::with('category')->get());
in CertificateResource
return [
'nickname' => $this->nickname,
'certificates' => $this->certificates,
'category_name'=>$this->whenLoaded('category', function () {
return $this->category->category_name;
}),
];

Yii2 display multiple images in gridview row

I want to display multiple images in a gridviews single row. For example: I have table A, Table B and table C.
Table A has my_id.
In Table B my_id is the foreign key. Along with my_id it has c_id.
Table C has c_id which is in reference in Table B.
Table C also has a filepath to display images.
in Table A i have my_id as follows:
1, 2, 3, 4, 5, 6.
In Table B i have my_id as follows.
1 ,1 ,1 ,2 ,3, 3.
In Table B i also have c_id as follows.
1, 2, 3, 4, 5, 6.
In table C my c_id's are:
1, 2, 3, 4, 5, 6. and these id's have filepath associated with each of them. They are different images.
Now my gridview should display 3 different images for my_id because of the foreign key constraints. but it displays only 1 image.
My code is below:
In my model
public function getPictogramsID()
{
$pictogramsID = SdsrefGhsPictograms::find()->where(['sdsref_id' => $this->sdsref_id])->all();
foreach ($pictogramsID as $picID){
return $picID->pictogram_id;
}
}
public function getPictogramPath()
{
$pictogramsID = GhsPictogram::find()->where(['pictogram_id' => $this->getPictogramsID()])->all();
foreach ($pictogramsID as $picID){
$pic = $picID->pictogram_filepath;
}
return $pic;
}
public function getPictogramUrl()
{
//var_dump($this->getPictogramPath()); exit();
return \Yii::$app->request->BaseUrl.'/web'.$this->getPictogramPath() ;
}
my index file grid view image code
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
return Html::img($data->getPictogramUrl(), ['alt'=>'myImage','width'=>'20','height'=>'30']);
},
],
I am also trying to add a bootstrap tool tip to this.. tool tip is displaying successfully but I think the looping is not not done in a correct way so it is repeating my images.
here is my updated gridview code.
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
$images = '';
// append all images
foreach($data->getPictogramName() as $name)
foreach ($data->getPictogramUrl() as $url)
$images = $images.Html::img($url,['alt'=>'','width'=>'30','height'=>'30', 'data-toggle'=>'tooltip','data-placement'=>'left','title' => $name ,'style'=>'cursor:default;']);
return $images;
}
],
You have few logical errors in model and grid view. In all these areas you are dealing with one item instead of three.
In your model
public function getPictogramsID()
{
$ids = [];
$pictogramsID = SdsrefGhsPictograms::find()->where(['sdsref_id' => $this->sdsref_id])->all();
foreach ($pictogramsID as $picID){
$ids[] = $picID->pictogram_id;
}
return $ids;// returning all three ids
}
public function getPictogramPath()
{
$pic = [];
$pictogramsID = GhsPictogram::find()->where(['pictogram_id' => $this->getPictogramsID()])->all();
foreach ($pictogramsID as $picID){
$pic[] = $picID->pictogram_filepath;
}
return $pic;
}
public function getPictogramUrl()
{
$url = [];
foreach($this->getPictogramPath() as $path):
$url[] = \Yii::$app->request->BaseUrl.'/web'.$path;
endforeach;
return $url; // returning al urls
}
Now in you view loop over all urls and append images with each url
[
'label' => 'Hazards',
'format' => 'raw',
'value' => function ($data) {
$images = '';
// append all images
foreach($data->getPictogramUrl() as $url):
$images = $images.Html::img($url, ['alt'=>'myImage','width'=>'20','height'=>'30']);
endforach;
return $images;
},
],

Resources