Flatten laravel nested relationship (parent to descendants) - laravel

I have a parent children relationship on my model. What I want is to flatten all those collection including all those descendants of parent.
[
{
'name': 'Parent',
'another_array' => [
{
'name': 'Another Array Name'
}
],
'children': [
{
'name': 'First Child',
'address': 'lorem ipsum',
'contact_no': '92390',
'another_array' => [
{
'name': 'Another Array Name'
}
],
'children': [
{
'name': 'First Descendant',
'address': 'lorem ipsum',
'contact_no': '92390',
'children': [
{
'name': 'Descendant Child',
'address': 'lorem ipsum',
'contact_no': '92390',
'children': [
{
'name': 'Last Descendant',
'address': '',
'contact_no': '',
'children': []
}
]
}
]
}
]
}
]
}
]
I tried the code below. However it only displays the second depth of children. It does not display the next children of the collection.
User.php
public function parent()
{
return $this->belongsTo(User::class, 'id', 'parent_id');
}
public function children()
{
return $this->hasMany(User::class,'parent_id', 'id');
}
public function descendants()
{
return $this->children()->with('descendants');
}
UsersController.php
public function index()
{
$parent = User::find(1);
$parent->with('descendants')->find($parent->id);
$descendants = $this->traverseTree($parent->descendants);
return response($descendants);
}
protected function traverseTree($subtree)
{
$descendants = collect([]);
foreach ($subtree->descendants as $descendant) {
$descendants->push($descendant);
if($descendant->descendants instanceof Collection) {
foreach ($descendant->descendants as $node) {
$this->traverseTree($node);
$descendants->push($node);
}
}
}
return $descendants;
}
Desired Output
[
{
'name': 'Parent',
'address': 'lorem ipsum',
'contact_no': '92390'
},
{
'name': 'First Child',
'address': 'lorem ipsum',
'contact_no': '92390'
},
{
'name': 'First Descendant',
'address': 'lorem ipsum',
'contact_no': '92390',
},
{
'name': 'Descendant Child',
'address': 'lorem ipsum',
'contact_no': '92390',
},
{
'name': 'Last Descendant' ,
'address': '',
'contact_no': '',
}
]
I am looking forward for your help.

from your collection response convert it to an array by chaining the toArray() method so you output becomes and a multi-dimensional array instead of nested objects as shown below...
$my_array = [
'name' => 'Parent',
'children' => [
'name' => 'First Child',
'address' => 'lorem ipsum',
'contact_no' => '234',
'children' => [
'name' => 'First Descendant',
'address' => 'lorem ipsum',
'contact_no' => '567',
'children' => [
'name' => 'Descendant Child',
'address' => 'lorem ipsum',
'contact_no' => '8890',
'children' => [
'name' => 'Last Descendant',
'address' => '',
'contact_no' => '',
'children' => []
]
]
]
]
];
then you can use this helper function below:
protected function _flattened($array)
{
$flatArray = [];
if (!is_array($array)) {
$array = (array)$array;
}
foreach($array as $key => $value) {
if (is_array($value) || is_object($value)) {
$flatArray = array_merge($flatArray, $this->_flattened($value));
} else {
$flatArray[0][$key] = $value;
}
}
return $flatArray;
}
when you dump the $result = $this->_flattened($my_array);, you will have output like...
[
0 => [
"name" => "Parent"
],
1 => [
"name" => "First Child",
"address" => "lorem ipsum"
"contact_no" => "234"
],
2 => [
"name" => "First Descendant"
"address" => "lorem ipsum"
"contact_no" => "567"
],
3 => [
"name" => "Descendant Child"
"address" => "lorem ipsum"
"contact_no" => "8890"
],
4 => [
"name" => "Last Descendant"
"address" => ""
"contact_no" => ""
],
];

Related

How can create a test in laravel for check load relationships

public function test_show_role_should_return_actions()
{
$this->actingAs($this->user, 'api');
$this->withHeaders(['Accept' => 'application/json',])
->get(route('roles.show', ['role' => $this->role->id]))
->assertJsonStructure([
"data" => [
"name",
"actions" => [
"name",
"code"
]
]
]);
}
when I run the test,I have this error:
Tests\Feature\Role\RoleTest::test_show_role_should_return_actions
Failed asserting that an array has the key 'name'.
Even when I remove the "name",I get an error for the "actions" key.
public function test_show_role_should_return_actions()
{
$this->actingAs($this->user, 'api');
$this->getJson(route('roles.show', ['role' => $this->role->id]))
->assertJsonStructure([
'data' => [
'*' => [
'name',
'actions' => [
'*' => [
'name',
'code'
]
]
]
]
]);
}

Laravel: How to access Object inside an array

I trying to return access the attributes inside a array of object and it is giving this error Exception: Property [id] does not exist on this collection instance.
Here is what I have tried:
protected function formatted($classroom)
{
return [
'courses' => [
'id' => $classroom->courses->id,
'name' => $classroom->courses->name,
'slug' => $classroom->courses->slug,
'coursteachers' => [
'id' => '$classroom->courses->coursteachers->id',
'email' => '$classroom->courses->coursteachers->email',
'uid' => '$classroom->courses->coursteachers->uid',
]
],
];
}
And here is the actual data:
"courses": [
{
"id": 1,
"name": "Analytics",
"slug": "analytics",
"status_id": 1,
"deleted_at": null,
"pivot": {
"classroom_id": 2,
"courses_id": 1
},
"coursteachers": [
{
"id": 3,
"uid": "S0120-46890",
"email": "teacher#vschool.com",
"user_type": "Teacher",
}]
}]
You need to iterate through courses and also courseteachers since they both represent an array but rather an object
protected function formatted($classroom)
{
$result = [];
foreach($classroom->courses as $course) {
$result[] = [
'courses' => [
'id' => $classroom->courses->id,
'name' => $classroom->courses->name,
'slug' => $classroom->courses->slug,
'coursteachers' => $this->getCourseTeachers($cours)
],
];
}
return $result;
}
private function getClassroomTeachers($classroom) {
$result = [];
foreach($classroom->courses as $cours)
{
foreach ($cours->coursteachers as $key => $teacher) {
$result[] = [
// 'coursteachers'=> [
'id' => $teacher->id,
'email' => $teacher->email,
'uid' => $teacher->uid,
'last_name' => $teacher->profile->last_name,
'first_name' => $teacher->profile->first_name,
'middle_name' => $teacher->profile->middle_name,
// ],
];
}
}
return $result;
}
Since courses is an array, you should pick an object using the proper index.
Foe example: try $classroom->courses[0]->id instead of $classroom->courses->id. Here, '0' is the index.
Also it is better if you check if the index exists before you do it. For an example.
'id' : isset($classroom->courses[$index]) ? $classroom->courses[$index]->id : ''
Edit:
In case of a Eloquent collection you should use $classroom->courses->get($index)->id instead of array like retrieval.

Search in Laravel Datatables

In my datatables, the search function works only on the name column. The name column ist different that the others. It means, the name column gives the value of the database back, while the others gives a value back with the function add column
In the column array there are the following values:
ยดยดยด
protected function getColumns()
{
return [
'status',
'name' => [
'title' => 'Name',
'orderable' => true,
'searchable' => true
],
'location' => [
'title' => 'Standort',
],
'department' => [
'title' => 'Abteilung',
'orderable' => true,
'searchable' => true
],
'division' => [
'title' => 'Bereich',
'orderable' => true,
'searchable' => true
],
'leader' => [
'title' => 'Verantwortlicher',
'orderable' => true,
'searchable' => true
],
'start_date' => [
'title' => 'Startdatum',
'searchable'=> true,
],
'end_date' => [
'title' => 'Enddatum',
'searchable'=> true
]
];
}
```
Why it doesn't search on all columns? What i have to do?
Try to search by using this code.
return Datatables::of($tasks)
->filter(function ($query) use ($request) {
$title = $request->title;
if (isset($title) && !empty($title)) {
$query->where('title', 'like', '%'.$title.'%');
}
})->make(true);
OR
return Datatables::of($tasks)
->filterColumn('title', function($query, $value) {
$query->whereRaw("title like ?", ["%{$value}%"]);
})->make(true);

yii2 pjax gridview issue

I am using gridview with pjax in yii2 advanced. When i add/update/delete any record the pjax loads the very first html of page. For example, i have 10 records after deleting a record there should be 9 records, but it still loads 10 records and this continues even if you have deleted all the records. Same goes for add and update as well.
But if i hit F5 then it loads the actual records fetched from the database table and if i do any of the actions again then it again shows 10 records.
Here is the Gridview code with Pjax
$this->registerJs("$('.js-delete').on('click', function(e) {
ajax_actions(this, true, {
'pjax_container': '#products-grid'
});
return false;
});");
Pjax::begin([
"id" => "products-grid",
"enablePushState" => false
]);
echo GridView::widget([
'dataProvider' => $dataProvider,
"summary" => '',
"rowOptions" => function($model) {
if($model->status == Common::STATUS_INACTIVE)
{
return ["class" => "inactive"];
}
},
'columns' => [
[
"class" => 'yii\grid\SerialColumn',
"headerOptions" => [
"class" => 'hidden-480'
],
"contentOptions" => [
"class" => 'hidden-480'
]
], [
"attribute" => "title",
"label" => "Product"
], [
"attribute" => "companies_id",
"label" => "Companies",
"value" => "companies.title"
], [
"attribute" => "product_types_id",
"header" => "Category",
"value" => "productTypes.title"
], [
"attribute" => "part_number",
"header" => "<span class=\"hidden-360\">Part </span>Number"
], [
"attribute" => "created_on",
"label" => "Added On",
"format" => ["date", "php:" . Common::DATETIME_FORMAT_DISPLAY],
"headerOptions" => [
"class" => 'textRight hidden-360'
],
"contentOptions" => [
"class" => 'textRight hidden-360'
]
], [
"class" => 'yii\grid\ActionColumn',
"header" => "Actions",
"headerOptions" => [
"class" => "textRight actions"
],
"contentOptions" => [
"class" => "textRight actions"
],
"template" => "{view} {edit} {delete}",
"buttons" => [
'view' => function($url, $model) {
return Html::a('<i class="' . Common::ICON_VIEW . '"></i> <span class="hidden-640">View</span>', ['view', 'token' => $model->token], ["class" => "call-ajax"]);
},
'edit' => function($url, $model) {
return Html::a('<i class="' . Common::ICON_EDIT . '"></i> <span class="hidden-640">Edit</span>', ['update', 'token' => $model->token], ["class" => "call-ajax"]);
},
'delete' => function($url, $model) {
return Html::a('<i class="' . Common::ICON_DELETE . '"></i> <span class="hidden-640">Delete</span>', ['delete', 'token' => $model->token], [
'class' => 'js-delete'
]);
}
]
]
]
]);
Pjax::end();
functions
function ajax_submit(element, pjax_container)
{
var form = $(element);
if(form.find('.has-error').length) return false;
$.post(form.attr('action'),form.serialize()).done(function(result){
var result = JSON.parse(result);
if(result.type=='error')
{
result.container = '.error-container';
messages(result)
}
else
{
if(pjax_container) $.pjax.reload({container: pjax_container});
$('.overlay').click();
result.container = '.message-container';
messages(result);
}
});
return false;
}
function ajax_actions(element, ask_me, params)
{
if(ask_me == true && ask() == false)
{
return false;
}
var $this = $(element);
var $params = {
callBacks: {
beforeSendAfter: function() {},
completeAfter: function() {}
}
};
if(params != undefined) $.extend(true, $params, params);
$.ajax({
type: 'POST',
url: $this.attr("href"),
cache: false,
beforeSend: function() {
if($params.callBacks.beforeSendAfter != undefined && $.isFunction($params.callBacks.beforeSendAfter)) $params.callBacks.beforeSendAfter();
},
success: function(response) {},
complete: function(response) {
var result = $.parseJSON(JSON.parse(response.responseText));
if(result.type=='error')
{
result.container = '.error-container';
messages(result)
}
else
{
result.container = '.message-container';
$('.overlay').click();
$.pjax.reload({container: $params.pjax_container});
messages(result);
}
if($params.callBacks.completeAfter != undefined && $.isFunction($params.callBacks.completeAfter)) $params.callBacks.completeAfter();
}
})
return false;
}

How can I get the value from method in my model?

I have some trouble displaying my result from a method in my model. this is the method:
/**
* #return \yii\db\ActiveQuery
*/
public function getSchoolFee()
{
return $this->hasOne(SibuSchool::className(), ['school_fee_id' => 'school_fee_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getScholarshipFee()
{
return $this->hasOne(SibuScholarship::className(),['scholarship_id'=>'scholarship_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getDiscountFee()
{
$scholarship= $this->getScholarshipFee();
$school= $this->getSchoolFee();
$data= new SibuPayment();
$data->total_payment=$school->amount-($scholarship->school_discount*$school->amount);
return $data;
}
i want to display the result from getDiscountFee method. I use this code on my View:
<?php echo $this->render('_search3', ['model' => $searchModel]); ?>
<?=
GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
[
// 'filterModel'=>'dataProviderSearch',
'attribute' => 'Nama',
'value' => 'virtual.student_name'
],
[
'attribute' => 'Uang Sekolah',
'value' => 'discountFee.total_payment'
],
[
'attribute' => 'Uang Kantin',
'value' => 'canteenFee.total_amount'
],
[
'attribute' => 'Uang Asrama',
'value' => 'dormitoryFee.amount'
],
[
'attribute' => 'Uang Perpustakaan',
'value' => 'libraryFee.amount'
],
[
'attribute' => 'Uang Les',
'value' => 'courseFee.amount'
],
[
'attribute' => 'Adm. Bank',
'value' => 'administration'
],
[
'attribute' => 'status',
'value' => function ($model) {
if ($model->scholarship_id == 0) {
return 'Tanpa Beasiswa';
} else if ($model->scholarship_id == 1) {
return 'Beasiswa Ekonomi Kategori A 100%';
} else if ($model->scholarship_id == 2) {
return 'Beasiswa Ekonomi Kategori B 50%';
} else if ($model->scholarship_id == 3) {
return 'Beasiswa Prestasi Kategori 50%';
} else if ($model->scholarship_id == 4) {
return 'Beasiswa Prestasi Kategori 25%';
} else {
return 'Beasiswa Prestasi Kategori 10%';
}
},
],
//'payment_class',
[
'attribute' => 'payment_class',
'value' => function ($model) {
if ($model->payment_class == 0) {
return 'Tanpa Golongan';
} else if ($model->payment_class == 1) {
return 'Golongan 1';
} else if ($model->payment_class == 2) {
return 'Golongan 2';
} else if ($model->payment_class == 3) {
return 'Golongan 3';
} else if ($model->payment_class == 4) {
return 'Golongan 4';
}
},
],
[
'attribute' => 'Total',
'value' => 'total_payment'
],
['class' => 'yii\grid\ActionColumn',
'template' => '{update_r}{del}',
'buttons' => [
'update_r' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
'title' => Yii::t('yii', 'View_r'),
]);
},
'del' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-trash"></span>', $url, [
'title' => Yii::t('yii', 'Del'),
]);
},
]
],
],
]);
?>
it works fine if i display this:
[
'attribute' => 'Uang Sekolah',
'value' => 'scholarshipFee.school_discount'
],
and this:
[
'attribute' => 'Uang Sekolah',
'value' => 'schoolFee.amount'
],
can anybody help me? if you need anything feel free to ask.
[
'attribute' => 'Uang Sekolah',
'value' => 'discountFee.total_payment'
],
discountFee is a function of a model, it will never work until you put there the model too. You can just do a
[
'attribute' => 'Uang Sekolah',
'value' => function ($model) {
return $model->getDiscountFee()->total_payment;
}
},
],

Resources