Recursive menu in laravel - php

I am building a recursive menu in the following way:
public static function tree()
{
return static::with(implode('.', array_fill(0, 100, 'children')))->where('parent_id', '=', '0')
->join('company_components', 'id', '=', 'company_components.component_id')
->where('company_components.company_id', '=', auth('api')->user()->with('company')->first()->company_id)
->orderBy('id')
->get();
}
I get something like this:
[
{
"id":1,
"parent_id":0,
"name":"Cuentas",
"url":"",
"icon":"fa fa-user",
"deleted_at":null,
"created_at":"2019-05-26 18:57:02",
"updated_at":"2019-05-26 18:57:02",
"company_id":1,
"component_id":1,
"children":[
{
"id":2,
"parent_id":1,
"name":"Crear Cuenta",
"url":"/account/create",
"icon":"fa fa-circle-o",
"deleted_at":null,
"created_at":null,
"updated_at":null,
"children":[]
}
]
}
]
How can I avoid the empty array of the last elements (which no longer have children)?
Is there any way that the element is not created if "children" if it does not have more children?

$this->cleanMenu($menu);
public function cleanMenu($tree)
{
foreach($tree as $t) {
if (!$t->children->count() > 0) {
unset($t['children']);
} else {
$this->cleanMenu($t->children);
}
}
}
If there is a solution solving from the model without the need to iterate again, it would be phenomenal!
I hope help

Related

PHP Map infinite objects

I have some struggles how to map infinite object in Laravel.
So I have one table Categories that I'm getting in controller like:
$find_parent = Category::where('slug', $slug)->with('childrenRecursive')->first();
childrenRecursive() works fine in that case.
And return of this object would be like (minified):
{
"id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"parent_id": "f0d29100-d2bc-48c8-89cf-985e0c03b8ac",
"children_recursive": [
{
"id": "30bf23a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"children_recursive": [
{
"id": "32312a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "30bf23a7-b28c-4s78-b873-1df589eebcb1",
}
]
},
{
"id": "32bf23a7-b28c-4s78-b873-1df589eebcb1",
"parent_id": "1cbd459a-ccc0-435b-b9a9-0433e2e9285b",
"children_recursive": []
}
]
}
So from this, I have to get all id's in every object. So I have foreach function in controller that looks like:
$find_parent = Category::where('slug', $slug)->with('childrenRecursive')->first();
$array = [];
foreach($find_parent->childrenRecursive as $l){
array_push($array, $l->id);
if($l->childrenRecursive){
$result = array_merge($array, $this->catTree($l->childrenRecursive));
}
}
return $result;
And catTree would look like:
public function catTree($list){
$tree = [];
foreach($list as $l){
array_push($tree, $l->id);
if($l->childrenRecursive){
array_merge($tree, $this->catTree($l->childrenRecursive));
}
}
return $tree;
}
So this is returning some of the objects, but not everything. What am I doing wrong here?

Filter relationship, based on array of integers

My model has many options, multiple fields of a single option store values as an array on integers.
I want to filter based on an integer is included.
in php similare to in_array(), but the array is stored in db, and the number is key word.
$productsWithOptions = [
{
"id":2,
"name":"mac 2",
"options":[
{
"id":4,
"price":99.9,
"product_id":2,
"colors":"[4,3,2]",
"sizes":"[5,3,2]"
},
{
"id":5,
"price":99.9,
"product_id":2,
"colors":"[4,4,5]",
"sizes":"[1,2,2]"
},
{
"id":7,
"name":"mac 7",
"options":[
{
"id":19,
"price":99.9,
"product_id":7,
"colors":"[6,3,4]",
"sizes":"[5,2,5]"
},
{
"id":20,
"price":1999.9,
"product_id":7,
"colors":"[1,6,6]",
"sizes":"[5,6,2]"
},
{
"id":21,
"price":1999.9,
"product_id":7,
"colors":"[4,6,5]",
"sizes":"[5,2,2]"
}
]
}
]
$products = Product::whereHas('options', function (Builder $query) use ($arr) {
$query->where('colors','like', "%2%" );
})->get();
This is how options table looks like in db.

Order Nested Eager Loading Eloquent Laravel

I have problem with eloquent query. I am using eager loading (one-to-many Polymorphic Relationship) to sortby 'historyable.date', JSON below.
[
{
"product_id":12,
"product_name": "Product C",
"historyable_type":"App\\Delivery",
"historyable":{
"id":2,
"date":"0303\/0404\/2020",
"for":"Customer A",
"created_at":"2020-04-02T09:46:48.000000Z",
}
},
{
"product_id":1,
"product_name": "Product A",
"historyable_type":"App\\Transfer",
"historyable":{
"id":1,
"date":"0202\/0404\/2020",
"for":"First Balance ****",
"created_at":"2020-04-02T07:30:45.000000Z",
}
},
{
"product_id":11,
"product_name": "Product B",
"historyable_type":"App\\Delivery",
"historyable":{
"id":2,
"date":"0303\/0404\/2020",
"for":"Customer B",
"created_at":"2020-04-02T09:46:48.000000Z",
}
}
]
And i try to get result like this
[
{
"product_id":1,
"product_name": "Product A",
},
{
"product_id":12,
"product_name": "Product C",
},
{
"product_id":11,
"product_name": "Product B",
}
]
Is it possible to run with Nested Lazy Eager Loading Laravel?
Product Model
public function track() {
return $this->hasMany('App\History');
}
History Model
public function historyable(){
return $this->morphTo()->orderBy('date', 'desc');
}
Product Controller
public function json_history(Product $product) {
$data = $product->with('track.historyable')
->where('id', $product->id)
->first();
return $data->track;
}
I Have database like this
Products Table
#id
#name
Histories Table
#product_id
#product_name
#historyable_id
#historyable_type
Deliveries Table
#id
#date
#for
Transfers Table
#id
#date
#for
public function json_history(Product $product) {
$data = Product::with('track.historyable')
->where('id', $product->id)
->first();
$arr = $data->track->toArray();
$date = array();
$create = array();
foreach ($arr as $key => $row)
{
$date[$key] = $row['historyable']['date'];
$create[$key] = $row['historyable']['created_at'];
}
array_multisort($date, SORT_DESC, $arr);
array_multisort($create, SORT_DESC, $arr);
return $arr;
}
I don't know but it works for me.. but i still find the shorter way for this..

php | dynamic api call

I am trying to create a dynamic endpoint for a API I am creating in order to include some data but only if it is required so I can use it in multiple places.
The idea is to have api.domain.com/vehicle to bring back basic vehicle information but if I did api.domain.com/vehicle?with=owners,history then the idea is to have a function which maps the owners and history to a class which will return data but only if it is required.
This is what I currently have.
public static function vehicle()
{
$with = isset($_GET['with']) ? $_GET['with'] : null;
$properties = explode(',', $with);
$result = ['vehicle' => Vehicle::data($id)];
foreach ($properties as $property) {
array_push($result, static::getPropertyResponse($property));
}
echo json_encode($result);
}
Which will then call this function.
protected static function getPropertyResponse($property)
{
$propertyMap = [
'owners' => Vehicle::owner($id),
'history' => Vehicle::history($id)
];
if (array_key_exists($property, $propertyMap)) {
return $propertyMap[$property];
}
return null;
}
However, the response I'm getting is being nested within a index, which I don't want it to be. The format I want is...
{
"vehicle": {
"make": "vehicle make"
},
"owners": {
"name": "owner name"
},
"history": {
"year": "26/01/2018"
}
}
But the format I am getting is this...
{
"vehicle": {
"make": "vehicle make"
},
"0": {
"owners": {
"name": "owner name"
}
},
"1": {
"history": {
"year": "26/01/2018"
}
}
}
How would I do this so it doesn't return with the index?
Vehicle::history($id) seems to return ['history'=>['year' => '26/01/2018']], ...etc.
foreach ($properties as $property) {
$out = static::getPropertyResponse($property) ;
$result[$property] = $out[$property] ;
}
Or your methods should returns something like ['year' => '26/01/2018'] and use :
foreach ($properties as $property) {
$result[$property] = static::getPropertyResponse($property) ;
}

cakephp and containable behaviour

I have the following json:
{
"Companies":[
{
"CompanyPersonTask":{
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"person_id":"2",
"task_id":"1"
},
"Company":{
"id":"502d1844-3638-44dc-824c-14f02e0cc009",
"name":"mostro",
"Office":[
{
"id":"502d1844-b90c-44f5-84c5-14f02e0cc009",
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"region":"Emilia-Romagna",
"city":"Rozzemilia",
"address":"-",
"phone":"-",
"legal_office":true
}
],
"CompanyPersonTask":[
{
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"person_id":"2",
"task_id":"1"
}
]
},
"Person":{
"id":"2",
"first_name":"Carlo",
"last_name":"Giusti",
"home_address":"Russi",
"job_address":null,
"phone":null,
"fax":null,
"mail":null,
"full_name":"Giusti Carlo",
"OfficePersonTask":[
],
"CompanyPersonTask":[
{
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"person_id":"2",
"task_id":"1"
}
]
},
"Task":{
"id":"1",
"short_name":"Proprietario",
"full_name":null,
"info":null,
"OfficePersonTask":[
],
"CompanyPersonTask":[
{
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"person_id":"2",
"task_id":"1"
}
]
}
}
]
}
That is created by this method, inside CompanyPersonTask model:
public function getCompaniesByRegion($region){
$this->recursive = 2;
return $this->find('all');
}
but I want my json to be formatted like this:
{
"Companies":[
{
"Company":{
"id":"502d1844-3638-44dc-824c-14f02e0cc009",
"name":"mostro",
"Office":[
{
"id":"502d1844-b90c-44f5-84c5-14f02e0cc009",
"company_id":"502d1844-3638-44dc-824c-14f02e0cc009",
"region":"Emilia-Romagna",
"city":"Rozzemilia",
"address":"-",
"phone":"-",
"legal_office":true
}
]
},
"Person":{
"id":"2",
"first_name":"Carlo",
"last_name":"Giusti",
"home_address":"Russi",
"job_address":null,
"phone":null,
"fax":null,
"mail":null,
"full_name":"Giusti Carlo"
}
}
]
}
How can I modify my method?
Use Containable behavior to filter result data.
For more explanation refer this link.
You can check unbindModel in cake php to remove the relation on the fly. This will prevent the data from the unwanted table. Check this.
<?php
$this->Model->unbindModel(
array('associationType' => array('associatedModelClassName'))
);

Categories