I am trying to view laravel datatable with nested row details on nested relationships. Here are the details of what I have done
Database relationships
I want the datatable to view all products on page load, then first level of row detail will show all components of a product. Second level of row detail will show all sub-components from a component.
This is the current result of my datatable view
As shown in the illustration, the datatable fails to show the rawmaterial name of a sub-component.
Here are my Eloquent models and relationships.
Product Model
protected $table = 'products';
public function components() {
return $this->hasMany('App\Component', 'product_id', 'id');
}
Component Model
protected $table = 'components';
public function comp_subcomps() {
/*The syntax ->with('subcomponents') seems WORKING fine in resulting
the second level row detail ( viewing the subcomponents of a component )*/
return $this->hasMany('App\CompToSubComp', 'component_id', 'id')->with('subcomponents');
}
CompToSubComp Model
protected $table = 'comp_subcomp';
public function subcomponents() {
/* This syntax ->with('rawmaterials') seems NOT WORKING to
get the material name in the second level row detail */
return $this->belongsTo('App\SubComponent', 'subcomponent_id', 'id')->with('rawmaterials');
}
SubComponent Model
protected $table = 'subcomponents';
public function rawmaterials() {
return $this->belongsTo('App\Rawmaterial', 'rawmaterial_id', 'id');
}
CONTROLLER
/* On page load */
public function viewTable() {
$products = Product::select();
return Datatables::of($products)
->addColumn('details_url', function($product) {
return url('/product/getdetaildata/' . $product->id);
})->make(true);
}
/* First level row detail ( showing components of a product ) */
public function getDetailsData($id)
{
$components = Product::find($id)->components();
return Datatables::of($components)
->addColumn('moredetails_url', function($component) {
return url('/product/getmoredetail/' . $component->id);
})->make(true);
}
/* Second level row detail ( showing sub-components of a component ) */
public function getMoreDetailsData($id) {
$compsubcomp = Component::find($id)->comp_subcomps();
return Datatables::of($compsubcomp )->make(true);
}
JS DATATABLE
/* On load: Its working */
var dt = $('#example').DataTable({
...
"columns": [
{ "data": "id", "name": "id" },
{ "data": "name", "name": "name"}
]
});
/* First level row detail: Its working */
var tabComponent = $('#' + tableId).DataTable({
...
"columns": [
{ "data": "id", "name": "id" },
{ "data": "name", "name": "name" }
]
});
/* Second level: This PROBLEM exists where RAWMATERIAL NAME is not showing */
$('#' + tableId).DataTable({
...
"columns": [
{ "data": "id", "name": "subcomponents.id" },
{ "data": "name", "name": "subcomponents.name" },
{ "data": "rawmaterial_id", "name": "subcomponents.rawmaterial_id" },
{ "data": "subcomponents.rawmaterials.name", "name": "subcomponents.rawmaterials.name" }
]
});
How to correctly view the relationship between 'rawmaterials' and 'subcomponents' inside my datatable?
Any help to identify my errors would be appreciated. Thanks in advance.
Related
I got the data json from API and I want to store them to my database.
data json sample :
{
"success": true,
"data": [
{
"status_employee_id": "1",
"name": "Manager"
},
{
"status_employee_id": "2",
"name": "Staff"
},
{
"status_employee_id": "3",
"name": "OB"
},
{
"status_employee_id": "4",
"name": "Fired"
},
{
"status_employee_id": "5",
"name": "Retired"
}
]
}
My model
class StatusEmployee extends Model
{
use HasFactory;
protected $table = 'status_employee';
protected $fillable = [
'status_employee_id','name'
];
public $timestamps = false;
}
I have tried use this in my controller
public function store()
{
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', 'http://xx.xx.xx.xx/tools/public/get_status_employee');
$datas = json_decode($res->getBody(), true);
foreach($datas as $data){
StatusEmployee::create([
'status_employee_id' => $data->status_employee_id,
'name' => $data->name,
]);
}
}
And I want to store the data json sample to my table status_employee. How to make it?
It's easy, Laravel Eloquent will do everything for you.
// If json is in string decode first
$data = json_decode($res->getBody(), true); // to array
// Eloquent approach
StatusEmployee::insert(#$data['data']);
I assume you use Laravel since you have mentioned Laravel in your tags.
DB::table('status_employee')->insert(json_decode($res->getBody(),true)['data']);
I am having this model in my Laravel-5.8 project:
Employee
class Employee extends Model
{
protected $table = 'employees';
protected $primaryKey = 'id';
protected $fillable = [
'staff_code',
'first_name',
'last_name',
'department_id',
];
public function department()
{
return $this->belongsTo('App\Department','department_id');
}
}
That is,:
App\Employee
Also I have an external api that comes in form of JSON get request.
https://api.employees.net/allemployees
I have viewed it with postman get request and I have something like this:
{
"ID": "1",
"StaffCode": "STC001",
"FirstName": "Japheth",
"LastName": "Shalom",
"DepartmentCode": "dep2",
},
{
"ID": "2",
"StaffCode": "STC002",
"FirstName": "Ahitophel",
"last_name": "Nedum",
"DepartmentCode": "dep1",
},
{
"ID": "3",
"StaffCode": "STC003",
"FirstName": "Joash",
"FirstName": "Nathan",
"DepartmentCode": "dep2",
},
and so on... this continues
Already I have created this function:
use App\Employee;
use App\Department;
public function index()
{
$client = new GuzzleHttp\Client();
$res = $client->request('GET','https://api.employees.net/allemployees');
$clientdatas = json_decode($res, true);
...
}
Apart from the id and staff_code that are UNIQUE, any of the others fields too can change from the source.
Since any of the fields can change at any time. How do I refresh the entire db, save new data and update changes?
Thank you
I could do something like this:
use App\Employee;
use App\Department;
public function index()
{
$client = new GuzzleHttp\Client();
$res = $client->request('GET','https://api.employees.net/allemployees');
$clientdatas = json_decode($res->getBody()->getContents(), true);
foreach($clientdatas as $clientdata)
{
$employee = Employee::firstOrNew(['id' => $clientdata['ID']]);
$employee->staff_code = $clientdata['StaffCode'];
$employee->first_name = $clientdata['FirstName'];
$employee->last_name = $clientdata['LastName'];
$employee->save();
}
}
Every time you will make an API call, create an instance of the employee model, or get the associated model if the ID exists. Then assign the new values and save your model. This way, you will be able to either create or update the models in a single loop without any complexities.
Don't know will it work or not but the logic you can keep.. In the other answer he is querying under a loop which is bad!
Hope It will Help you a little bit
public function index() {
$client = new GuzzleHttp\Client();
$res = $client->request('GET','https://api.employees.net/allemployees');
$clientdatas = json_decode($res->getBody()->getContents(), true);
// create a blank array for insert
$insert_arr = [];
foreach($clientdatas as $clientdata) {
$make_array = [
'id' => $clientdata['ID'],
'staff_code' => $clientdata['StaffCode'],
'first_name' => $clientdata['FirstName'],
.
.
];
array_push($insert_arr, $make_array);
// Now the the $insert_arr will ready for insert
}
// Here you have to check its duplicate or not
$exist_in_db = Employee::all('id')->toArray(); // get the id array from db
// do the stuff for unset the duplicate
$final_id = array_map(function($value) {
return $value['id'];
}, $team_id);
unset($exist_in_db);
if(count($final_id) > 0){
foreach ($insert_arr as $key => $value) {
if (in_array($value['id'], $final_id)) {
unset($insert_arr[$key]);
} else {
array_push($final_id, $value['id']);
}
}
}
// finally insert it here
if (count($insert_arr) > 0) {
Employee::insert($insert_arr);
}
}
Let me know its helpful or not!
Here is my JSON return
{
"product": {
"id": 1,
"name": "Glory Cookware",
"barcode": "1234567891234",
"local_code_id": 1,
"category_id": 1,
"description": null
},
"calculations": {
"product_quantity_in_warehouses": 1580,
"avgSellingPriceBeforeTax": 901.7085714285713,
"avgSellingPriceAfterTax": 1027.9477714285713,
"creditPriceInWarehousesBeforeTax": 1530828,
"creditPriceInWarehousesAfterTax": 1745143.92
}
}
What I am trying to do:
In the Product.php model I am trying to hide calculations.creditPriceInWarehousesBeforeTax.
What I have tried:
I have tried to use toArray() method in the Product.php mode.
public function toArray()
{
$hidden = ['calculations.creditPriceInWarehousesBeforeTax'];
// Also tried ['creditPriceInWarehousesBeforeTax']
$this -> hidden = $hidden;
return parent::toArray();
}
Expected behavior VS what happened
The JSON result should not return the calculations.creditPriceInWarehousesBeforeTax but what happened is that it return with the JSON.
NOTE
calculations are a mutator that I have created to append it as an attribute.
So, i have a mongo database filled(21k enteries) with columns like action(there are 5 different actions) id, time, etc.
I need to get the name of every action, and how many times does this action occur. For example: USERPROPERTY_CHANGED - 755
I have tried pretty much everything in here Laravel Eloquent groupBy() AND also return count of each group
Then i tried to make another collection, where i input the fields one, by one, and then fetch them, my migration looks like this:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class actionPopularity extends Migration
{
public function up()
{
Schema::create('actionPopularity', function (Blueprint $collection) {
$collection->Increments('id');
$collection->string('action');
$collection->integer('count');
});
}
public function down()
{
Schema::dropIfExists('actionPopularity');
}
}
But the collection that this generated had only one field - id.
heres something that kind of works (it shows that it can work with the database)
Controller:
public function database_test()
{
$actions = Action::groupBy('action')->get();
return view('database_test',compact('actions'));
}
View:
{{$actions}}
Output:
[
{
"_id":
{
"action":"OBJECT_INSERT"
},
"action":"OBJECT_INSERT"
},
{
"_id":
{
"action":"OBJECT_MODIFY"
},
"action":"OBJECT_MODIFY"
},
{
"_id":
{
"action":"null"
},
"action":"null"
},
{
"_id":
{
"action":"USERPROPERTY_CHANGED"
},
"action":"USERPROPERTY_CHANGED"
},
{
"_id":
{
"action":"OBJECT_DELETE"
},
"action":"OBJECT_DELETE"
}
]
Ultimately i want to get two arrays, one with action names, and another one with the amount of times that this action has been called, to put it in a chart.
Please try the following code:
public function database_test()
{
$actions = Action::select('action', 'count')->groupBy('action')->get()->toArray();
return view('database_test',compact('actions'));
}
This works:
public function database_test()
{
$actions = Action::groupBy('action')->get();
$action_names = array();
foreach ($actions as $action)
{
array_push($action_names, $action->action);
}
$action_count = array();
foreach ($actions as $action)
{
$count = Action::where('action','like',$action->action)->count();
array_push($action_count, $count);
}
return view('database_test',compact('actions', 'action_names', 'action_count'));
}
view:
{{print_r($action_names)}}
{{print_r($action_count)}}
I am using laravel 4.1 to build an api. I have pivot a table which is working fine. But the response comes with pivot attributes which i don't want. as you will see in my example i have to two tables name: trips and users. I don't want to see pivot table attributes in my response. Here is the example:
[
{
"id": 140,
"name_first": "hasan",
"name_last": "hasibul",
"profile_image": "/assets/images/default-profile-img.png",
"created_at": "2013-09-18 08:19:50",
"last_login": "2013-12-26 11:28:44",
"status": "active",
"last_update": "2013-10-15 13:40:47",
"google_refresh_token": null,
"is_admin": 1,
"updated_at": null,
"pivot": {
"trip_id": 200,
"user_id": 140
}
}
This is my User Model:
public function trips(){
return $this->belongsToMany('Trip');
}
This is my trip model:
public function users(){
return $this->belongsToMany('User');
}
This is my controller:
public function index($tripId)
{
$userCollection = Trip::find($tripId)->users;
return $userCollection;
}
This is my route:
//get all the users belongs to the trip
Route::get('trips/{tripId}/users', array(
'as' => 'trips/users/index',
'uses' => 'TripUserController#index'
));
is there any way i can remove pivot attributes using laravel or i have to use php ?
Use the $hidden property of the model, you can add attributes or relations to it and the pivot is basicly acts as a relation.
class Foo extends Eloquent
{
protected $hidden = array('pivot');
public function bars()
{
return $this->belongsToMany('Bar');
}
}
If you want to remove just any one column from the response, then you can try something like this:
In you Model:
public function toArray()
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
unset($attributes['pivot']['user_id']);
return $attributes;
}
This way you will get only attribute required.
You can add it to your "hidden" array. At Model page
protected $hidden = [
'pivot'
];
As mentioned above you can remove the pivot attribute from the response, by adding the following to the related model.
protected $hidden = [
'pivot'
];
Moreover, in case you want to select specific fields from the pivot to be displayed in the related user object you can add this to your controller using Laravel 5.8. This works also when you hide the pivot information with the above code snippet.
public function index(Trip $trip)
{
return $trip->users()->select(['trip_id'])->paginate();
}
and you will receive something objects where the trip_id is added to the user object.
{
"data": [
{
"id": 140,
"trip_id": 200,
"name_first": "hasan",
"name_last": "hasibul",
"profile_image": "/assets/images/default-profile-img.png",
"created_at": "2013-09-18 08:19:50",
"last_login": "2013-12-26 11:28:44",
"status": "active",
"last_update": "2013-10-15 13:40:47",
"google_refresh_token": null,
"is_admin": 1,
"updated_at": null,
"pivot": {
"trip_id": 200,
"user_id": 140
}
}
]
}