I have categories with id, parent_id, slug , pivot table category_language which columns are id,category_id,language_id,value
As you can see I can translate parent category, but can't send desired $lang_id to children translations, so each children having all translations
here is what I get:
{
"id": 1,
"parent_id": 0,
"slug": "personal-computers",
"created_at": "2019-12-27 15:05:31",
"updated_at": "2019-12-27 15:05:31",
"children": [
{
"id": 3,
"parent_id": 1,
"slug": "accessories-for-pc",
"created_at": "2019-12-27 15:05:32",
"updated_at": "2019-12-27 15:05:32",
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 3,
"language_id": 1,
"value": "Acc for PC",
"id": 7
}
},
{
"id": 2,
"code": "ru",
"name": "Русский",
"pivot": {
"category_id": 3,
"language_id": 2,
"value": "Аксессуары для ноутбуков и ПК",
"id": 8
}
},
{
"id": 3,
"code": "ro",
"name": "Romana",
"pivot": {
"category_id": 3,
"language_id": 3,
"value": "aksessuari-dlya-noutbukov-i-pk-ro",
"id": 9
}
}
]
}
],
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 1,
"language_id": 1,
"value": "PC",
"id": 1
}
}
]
}
Controller:
return Category::with('children')
->with(array('translations'=>function($query) use ($lang_id){
$query->where('language_id',$lang_id);
}))
->where('parent_id',0)->first();
Model
class Category extends Model
{ ..
public function translations()
{
return $this->belongsToMany('App\Models\Translation','category_language', 'category_id' ,'language_id' )->withPivot('value','id');
}
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations');
}
}
you can add condition in children method
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations')->where('language_id', 1);
}
Dry7 answer was close to the one I've implemented later, so I upvoted him.
Finally in model I've added: ...->where('language_id',helper_SetCorrectLangIdForQuery());
and function helper_SetCorrectLangIdForQuery is using global helper of Laravel request()->lang . If lang=enz, than it takes default language from another helper.
Related
This link Laravel filter one to many relationship has a solution related to my question but I don't know why the solutions are not working for me.
I am currently selecting all the offtaker sites and the apartments the site have but I want to filter the apartment to only apartment that the apartment id is equal to the offtakers apartment_id
Below are my model setup
offtaker model
public function sites(){
return $this->belongsTo(Sites::class,'site_id','id')->select('id','name');
}
site model
public function apartment(){
return $this->hasMany(Apartment::class,'site_id','id');
}
offtaker controller
return Offtakers::select(['id','site_id','offtaker_id','company_id','apartment_id'])
->with('sites',
)->with('sites.apartment',function($query){
$query->where(function($query){
$query->whereRaw("id = (select apartment_id from offtakers where apartment_id=apartments.id)");
});
})->get();
Current out put
{
"id": 2,
"site_id": "1",
"offtaker_id": 6,
"apartment_id": "8",
"sites": {
"id": 1,
"name": "first site",
"apartment": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
},
{
"id": 55,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment eleven",
"apartment_description": "test descriptions",
"amount": 550000
}
]
}
},
Expected out put
{
"id": 2,
"site_id": "1",
"offtaker_id": 6,
"apartment_id": "8",
"sites": {
"id": 1,
"name": "first site",
"apartment": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
},
]
}
},
For you, I suggest you add this package https://github.com/staudenmeir/eloquent-has-many-deep.
This will solve the 2 deep relationships Offtaker - Site - Apartment
Add new relationship in Offtaker model
public function apartments()
{
return $this->hasManyThrough(Apartment::class, Site::class);
}
And to use it:
return Offtaker::select(['id','site_id','offtaker_id','company_id','apartment_id'])
->with([
'sites' => function ($q) {
$q->where('id', 'offtakers.site_id');
},
'apartments' => function ($q) {
$q
->where('site_id', 'offtakers.site_id')
->where('apartment_id', 'offtakers.apartment_id');
}
])
->get();
However the result might a bit different that want you want
{
"id": 2,
"site_id": 1,
"offtaker_id": 6,
"apartment_id": 8,
"sites": [
{
"id": 1,
"name": "first site",
}
],
"apartments": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
}
]
},
....
I hope this can help you out.
This is my first ever Laravel project, please bear with us. I am trying to display a parent->child hierarchy (not recursive), something akin to below:
Category 1
----List item
----------Resource 1
----List item
----------Resource 2
Category 2
-----List item 1
-----List item 2....etc
I can't get my head around how to write the query to get the resources linked to the subStatement, not the top category, basically, an inner join related to SubStatement. It is currently displayed as so;
{
"id": 1,
"name": "Category 1: Practising plumbing overview",
"description": "<p>Category 1: Practising plumbing overview</p>",
"created_at": "2022-01-06 15:48:49",
"updated_at": "2022-01-06 15:48:49",
"deleted_at": null,
"plumbing_area_id": 1,
"category_id": 1,
"SubStatement": [
{
"id": 1,
"title": "Plying narrative for Category 1",
"description": "<p>Body of content.</p>",
"theme_id": 1,
"plumbing_area_id": 1
},
{
"id": 2,
"title": "Lay narrative for Category 1",
"description": "<p>body description.</p>",
"theme_id": 1,
"plumbing_area_id": 1
}
],
"resources": []
},
{
"id": 2,
"name": "Category 2: AAV Practising plumbing introduction",
"description": "<p><strong>Category 2:</strong> AAV Practising plumbing introduction</p>",
"created_at": "2022-01-06 15:49:12",
"updated_at": "2022-01-06 15:49:12",
"deleted_at": null,
"plumbing_area_id": 1,
"category_id": 1,
"SubStatement": [
{
"id": 3,
"title": "Plying narrative for Category 2",
"description": "<p>Body of content;</p>",
"theme_id": 2,
"plumbing_area_id": 1
},
{
"id": 4,
"title": "Lay narrative for Category 2",
"description": "<p>Body of content</p>",
"theme_id": 2,
"plumbing_area_id": 1
}
],
"resources": []
},
{
"id": 3,
"name": "Category 3: AAV Practising plumbing design",
"description": "<p><strong>Category 3: </strong>AAV Practising plumbing design</p>",
"created_at": "2022-01-06 15:49:47",
"updated_at": "2022-01-06 15:49:47",
"deleted_at": null,
"plumbing_area_id": 1,
"category_id": 1,
"SubStatement": [
{
"id": 5,
"title": "Plying narrative for Category 3",
"description": "<p>Body of content;</p>",
"theme_id": 3,
"plumbing_area_id": 1
},
{
"id": 6,
"title": "Lay narrative for Category 3",
"description": “Blah blah</p>",
"theme_id": 3,
"plumbing_area_id": 1
}
],
"resources": []
},
{
"id": 4,
"name": "Category 4: another “category,
"description": “Body</p>",
"created_at": "2022-01-06 15:50:04",
"updated_at": "2022-01-06 15:50:04",
"deleted_at": null,
"plumbing_area_id": 1,
"category_id": 1,
"SubStatement": [
{
"id": 7,
"title": "Plying narrative for Category 4",
"description": "<p>sdfsdfsdf;</p>",
"theme_id": 4,
"plumbing_area_id": 1
},
{
"id": 8,
"title": "Lay narrative for Category 4",
"description": "<p> sdfsdfsdfsdf</p>",
"theme_id": 4,
"plumbing_area_id": 1
}
],
"resources": []
},
]
I am looking for something like below instead, please note where the resource node is:
[
{
"id": 1,
"name": "Category 1: Practising plumbing overview",
"description": "<p>Category 1: Practising plumbing overview</p>",
"created_at": "2022-01-06 15:48:49",
"updated_at": "2022-01-06 15:48:49",
"deleted_at": null,
"plumbing_area_id": 1,
"category_id": 1,
"SubStatement": [
{
"id": 1,
"title": "Plying narrative for Category 1",
"description": "<p>Body of content.</p>",
"theme_id": 1,
"plumbing_area_id": 1,
"resources": [
{
"id": 1,
"resource_title": "The name of the resource"
}
]
}
]
}
]
//Themes Model
{
return $this->hasMany(Statement::class, 'theme_id', 'id');
}
public function resources()
{
return $this->belongsToMany(Resource::class);
}
//the Controller below is outputting the the JSON above
{
$output = Theme::where ( 'category_id', '=', $category_id )
->with(['subStatement' =>
fn ($query) =>
$query->select('id','title','description','theme_id','therapy_area_id')
])
->with(['resources' =>
fn ($query) =>
$query->select('id','temporary_url')])
->get();
}
// I attempted writing something like this. It threw a "Integrity constraint violation: 1052 Column 'id' in field list is ambiguous" error
{
return $this->hasOneThrough(Statement::class, Resource::class);
}
Your main issue is that you need to nest the query.
I will first adding the relationships to your models according to your schema.
Then, I will add the base query for the nesting if I understood what you're trying to achieve correctly.
Models
Theme.php
public function subStatements()
{
return $this->hasMany(Statement::class); // 1-N
}
public function statements()
{
return $this->belongsToMany(Statement::class); // N-N
}
public function resources
{
return $this->belongsToMany(Resource::class); // N-N
}
Statement.php
public function theme()
{
return $this->belongsTo(Theme::class); // 1-N
}
public function themes()
{
return $this->belongsToMany(Theme::class); // N-N
}
public function resources
{
return $this->belongsToMany(Resource::class); // N-N
}
Resource.php
public function themes()
{
return $this->belongsToMany(Theme::class); // N-N
}
public function statements()
{
return $this->belongsToMany(Statement::class); // N-N
}
Query
$themes = Theme::query()
->where('category_id', $category_id)
->with([
'subStatements' => fn ($query) => $query->with('resources')
])
->get();
I am working with laravel and I have basically these 3 related models in the following way: person model (Person.php), task model (Task.php) and post model (Post.php).
Person.php
public function tasks()
{
return $this->hasMany(Task::class);
}
public function posts()
{
return $this->hasMany(Post::class);
}
Task.php
public function person()
{
return $this->belongsTo(Person::class);
}
Post.php
public function person()
{
return $this->belongsTo(Person::class);
}
this is the way in which the models are related, now I have a method to recover a person and their tasks and posts that looks like this:
Person::with(['tasks','posts'])->findOrFail($id);
this returns the formatted data similar to these (it's an example):
{
"id": 1,
"name": "jhon",
"email": "jhon#gmail.com",
"created_at": "2018-09-14 12:07:35",
"updated_at": "2018-09-14 12:07:38",
"tasks": [
{
"id": 1,
"description": "task description",
"person_id": 1,
"created_at": "2018-09-18 21:07:48",
"updated_at": "2018-09-19 20:47:37",
},
{
"id": 2,
"description": "task description",
"person_id": 1,
"created_at": "2018-09-19 00:15:45",
"updated_at": "2018-09-20 15:28:58",
}
],
"posts": [
{
"id": 1,
"title": "post title",
"person_id": 1,
"created_at": "2018-09-14 12:08:52",
"updated_at": "2018-09-14 16:21:03"
},
{
"id": 3,
"title": "post title",
"person_id": 1,
"created_at": "2018-09-17 18:33:51",
"updated_at": "2018-09-17 18:33:51"
}
]
}
my question is this: is it possible to separate the result of the tasks and posts models in a different array, if this is possible as I should do ?, I want to have a result like this:
[
{
"id": 1,
"description": "task description",
"person_id": 1,
"created_at": "2018-09-18 21:07:48",
"updated_at": "2018-09-19 20:47:37",
},
{
"id": 2,
"description": "task description",
"person_id": 1,
"created_at": "2018-09-19 00:15:45",
"updated_at": "2018-09-20 15:28:58",
},
{
"id": 1,
"title": "post title",
"person_id": 1,
"created_at": "2018-09-14 12:08:52",
"updated_at": "2018-09-14 16:21:03"
},
{
"id": 3,
"title": "post title",
"person_id": 1,
"created_at": "2018-09-17 18:33:51",
"updated_at": "2018-09-17 18:33:51"
}
]
$a = DB:table('posts')->get();
$b = DB:table('tasks')->get();
$c = array_merge($a,$b);
see doc:
https://laravel.com/docs/5.7/queries
I am working on a system where I need many users can have many accounts and they can have multiple roles per account.
How can I set that up in eloquent.
I am currently having in User model:
public function accounts() {
return $this->belongsToMany('App\Models\Account');
}
public function roles() {
return $this->belongsToMany('App\Models\Account')->withPivot('role');
}
I have a pivot table user_account where the role is defined.
My problem is that when I go
$user = User::with('accounts')
->with('roles')
->where('id', $user['id'])
->first();
My output is:
{
"id": 1,
"name": "John Doe",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"accounts": [
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1
}
},
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1
}
}
],
"roles": [
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1,
"role": "SYSTEMADMINISTRATOR"
}
},
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1,
"role": "USER"
}
}
]
}
What I want is for the roles to contain just the data from the pivot table with user_id, account_id and the role name. Any pointers? I would also like the accounts output to contain just one account.
Through extensive searching on the net I actually found a solution.
You need to make an AccountUser model and add the role to the account_user table.
In the User class you set up these relations:
public function roles() {
return $this->hasMany('\App\Models\AccountUser');
}
public function accounts() {
return $this->hasManyThrough('\App\Models\Account', '\App\Models\AccountUser', 'id', 'id');
}
And in the AccountUser class
public function user() {
return $this->belongsTo('App\Models\User');
}
public function account() {
return $this->belongsTo('App\Models\Account');
}
And in the Account class
public function accountUser() {
return $this->hasMany('\App\Models\AccountUser');
}
Now I can do
$user = User::with('roles')
->with('accounts')
->where('email', $data['email'])
->first();
And I get my expected output :)
Hope this helps someone...
Given the following two arrays, how can they be merged efficiently to result in the third array?
productData
$productData =
[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]
products
$products =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}
productsFinal ($productData merged into $products - based on matching combo of product_id/product_id and type/product_type)
$productsFinal =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6,
// How to merge product data and wrap with "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
},
{
"id": 6,
"type": "electronic",
"product_id": 5,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
}
},
{
"id": 9,
"type": "kitchen",
"product_id": 4,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
}
]
}
I tried different things for the outcome in a foreach loop but still cannot get it to render as intended:
foreach($productData as $productDataItem) {
// when $productDataItem.product_id == $product.product_id && $productDataItem.type == $product.product_type
// move the matching $productDataItem object into matching $product object, wrapped in a new "data" key
}
I don't know Laravel too well. However you can join your data objects quite easily:
<?php
$productData = json_decode('[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]');
$products = json_decode('{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}');
// combine both data objects
foreach($products->data As &$p) {
foreach($productData As $d) {
if(property_exists($p, "product_id") && property_exists($d, "product_id") && property_exists($p, "type") && property_exists($d, "type")) {
if($p->product_id==$d->product_id && $p->type==$d->type) {
//$p = (object) array_merge((array) $p, (array) $d);
$p->data = $d; // updated answer
continue;
}
}
}
}
echo("<pre>");
echo json_encode($products, JSON_PRETTY_PRINT);
?>
You can test the code here: http://sandbox.onlinephpfunctions.com/code/98a50c35ee32c30f0d2be1661f7afb5895174cbe
Update: http://sandbox.onlinephpfunctions.com/code/aeebfdcf4f4db5e960260e931982570cfed19e0e
I would suggest to check this package dingo/api. I assume you want to display some kind of JSON response. Take a look at Transformers. You can do something like this :
<?php
namespace App\Http\Transformers;
use App\Http\Controllers\ProductData;
use League\Fractal\TransformerAbstract;
class ProductsDataTransformer extends TransformerAbstract
{
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(ProductData $productdata)
{
return [
'id' => $productdata->id,
'product_type' => $productdata->product_type,
'product /*or data*/' => Product::find($productdata->product_id),
];
}
}
This would find the product by it's ID and look like this :
{
"id": 4,
"product_type": "electronic",
"product" {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
},
},
You can then also create a transformer for Product to take care of your specs attribute to do the same thing.