Why in children array the parent property does not serialize? - php

I have an issue with serializing data in Symfony/ApiPlatform. For example I have such entity:
<?php
namespace App\Entity;
....
....
#[ApiResource(
normalizationContext: ['groups' => ['read']]
)]
class Test
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[Groups('read')]
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
private ?self $parent = null;
#[Groups('read')]
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
private Collection $children;
#[Groups('read')]
#[ORM\Column(length: 255)]
private ?string $title = null;
....
....
}
That is, basically this entity has the parent property and the children property. Each entity can have an ancestor and that ancestor can have another ancestor and so on (max depth=3 I think). And each entity can have children that have children as well and so on, AND each children can have an ancestor.
I created some records in db for this test
When I try to get a collection via GET endpoint I get JSON with such data:
{
....
"hydra:member": [
{
"#id": "/api/tests/1",
"#type": "Test",
"children": [
{
"#id": "/api/tests/2",
"#type": "Test",
"parent": "/api/tests/1",
"children": [
{
"#id": "/api/tests/3",
"#type": "Test",
"parent": "/api/tests/2",
"children": [],
"title": "test3"
}
],
"title": "test2"
}
],
"title": "test1"
},
{
"#id": "/api/tests/2",
"#type": "Test",
"parent": {
"#id": "/api/tests/1",
"#type": "Test",
"children": [
"/api/tests/2"
],
"title": "test1"
},
"children": [
{
"#id": "/api/tests/3",
"#type": "Test",
"parent": "/api/tests/2",
"children": [],
"title": "test3"
}
],
"title": "test2"
},
{
"#id": "/api/tests/3",
"#type": "Test",
"parent": {
"#id": "/api/tests/2",
"#type": "Test",
"parent": {
"#id": "/api/tests/1",
"#type": "Test",
"children": [
"/api/tests/2"
],
"title": "test1"
},
"children": [
"/api/tests/3"
],
"title": "test2"
},
"children": [],
"title": "test3"
}
],
....
}
As you can see each item has parent and children, it's okey. But if we'll look at children then we'll see the parent property and this property is in IRI format. This is my issue. It should be like object
I've already tried to any groups for this. Alse I've tried to add #MaxDepth to each property, for instance:
#[MaxDepth(5)]
#[Groups('read')]
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
private Collection $children;
#[MaxDepth(5)]
#[Groups('read')]
#[ORM\Column(length: 255)]
private ?string $title = null;
And also I enabled max_depth
#[ApiResource(
normalizationContext: [
'enable_max_depth' => 'true',
'groups' => ['read']
]
)]
but It didn't work. I've tried to write custom normalizer but without success.
I expected that in each 'children' property will be 'parent' property like object instead of IRI string. How to solve it?

Related

How to Count Nested Child from single table Laravel

Relation method
User model:
public function children()
{
return $this->hasMany(self::class, 'referrer_id')->with('children');
}
Controller Method:
$user = User::with('children')->where('id',$this->request->UserId)->get();
Response
{
"status": true,
"status_code": "Success",
"message": true,
"payload": {
"profile": [
{
"id": 1,
"referrer_id": null,
"name": "beciwerugy",
"username": "lahaky",
"email": "figo#mailinator.com",
"children": [
{
"id": 3,
"referrer_id": 1,
"name": "asd",
"username": "asde",
"email": "asda#asd.com",
"children": [
{
"id": 4,
"referrer_id": 3,
"name": "asd",
"username": "asde12",
"email": "as12a#asd.com",
]
}
]
}
]
}
]
}
]
}
}
I want to count all nestedchild at once
Currently im getting all nested child in ther list
but i just want count

How to read all product of sub categories

I need to return products of a particular category along with the following products in the subcategory of the category and I use laravel polymorphic for this work, can you help me how to return products?
I use from a recursive function for this work and this work and return to me but Not optimal
Product.php
/**
* Get all of the owning product_able models.
*/
public function product_able()
{
return $this->morphTo();
}
Category.php
/**
* Get the category's product.
*/
public function product()
{
return $this->morphOne(Product::class, 'product_able');
}
/**
* Get all of the categories for the children.
*/
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the category that owns the parent.
*/
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
CategoryController.php
public function detail()
{
$products = new Collection();
$category = Category::ofUuid(request('category_uuid'))->ofType(config('constants.category.type.main'))->active()->first();
$products->push($category->product);
$subCategory = CategoryDetailResource::collection($category->children);
$products->push(self::check($category));
$productsFlatten = $products->flatten();
$productsFilterNull = Utility::filterNullValue($productsFlatten);
$productsPagination = Utility::paginate_collection($productsFilterNull, 15);
$productsResource = ProductResource::collection($productsPagination);
return response()->json([
'sub_categories' => $subCategory,
'products' => $productsResource
]);
}
public function check($categories)
{
$data = [];
foreach ($categories->children as $category) {
if ($category->product) {
if ($category->product->status == config('constants.product.status.active'))
$data[] = [
'product' => $category->product,
'children' => self::check($category),
];
$data[] = [
'children' => self::check($category),
];
}
}
return $data;
}
CategoryResource.php
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'uuid' => $this->uuid ?? '',
'title' => $this->describe->title ?? '',
'path' => $this->file->path ?? config('constants.default.category.image'),
'children' => CategoryDetailResource::collection($this->children()->ofType(0)->get() ?? ''),
];
}
my output:
{
"sub_categories": [
{
"uuid": "afcac5f8-95d4-42f5-af7e-b7db943c5d09",
"title": "other",
"path": "https://dkstatics-public.digikala.com/digikala-products/2326879.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": [
{
"uuid": "c7b5d0f8-25af-437e-ac7e-4103c8bc1960",
"title": "cover",
"path": "https://dkstatics-public.digikala.com/digikala-products/5157933.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "9cd0c171-e46d-4e1d-b3b4-5c71a79fdd39",
"title": "power",
"path": "https://dkstatics-public.digikala.com/digikala-products/834627.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "f6b728a7-674f-44b8-9a8c-70d891283af7",
"title": "headphone",
"path": "https://dkstatics-public.digikala.com/digikala-products/2737272.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "a8160991-9750-4473-9252-dc4b53eef62f",
"title": "holder",
"path": "https://dkstatics-public.digikala.com/digikala-products/2027918.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": [
{
"uuid": "3216cd11-ba0e-431f-96b1-6665b9b6b420",
"title": "car",
"path": "https://dkstatics-public.digikala.com/digikala-products/4635214.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
}
]
}
]
},
{
"uuid": "a017d012-313c-47d9-8146-965ba0fb4bb3",
"title": "mobile",
"path": "https://dkstatics-public.digikala.com/digikala-products/4560689.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "eff6c9ff-7f1a-401b-ba90-df2b3e3b3567",
"title": "camera",
"path": "https://dkstatics-public.digikala.com/digikala-products/266661.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
},
{
"uuid": "a935ebb0-5392-4d56-b30d-942080ae1957",
"title": "laptop",
"path": "https://dkstatics-public.digikala.com/digikala-products/5177558.jpg?x-oss-process=image/resize,m_lfit,h_500,w_500/quality,q_80",
"children": []
}
],
"products": [
null
]
}

Laravel Resource - Parents with Childrens Categories

I want to load my childrens categories in the parent with Laravel Resource.
My json is like that :
"data": [
{
"id": 2,
"slug": "child-categorie-0",
"name": "Child Catégorie 0",
"parent_id": 1
},
{
"id": 3,
"slug": "child-categorie-1",
"name": "Child Catégorie 1",
"parent_id": 1
},
{
"id": 1,
"slug": "parent-categorie-0",
"name": "Parent Catégorie 0",
"parent_id": null
},
],
I would like to be like that :
"data": [
{
"id": 1,
"slug": "parent-categorie-0",
"name": "Parent Catégorie 0",
"childrens": [
{
"id": 2,
"slug": "child-categorie-0",
"name": "Child Catégorie 0",
},
{
"id": 3,
"slug": "child-categorie-1",
"name": "Child Catégorie 1",
}
]
}
],
App\Http\Resources\Category.php
public function toArray($request)
{
return [
'id' => $this->id,
'slug' => $this->slug,
'name' => $this->name,
'parent_id' => $this->parent_id
];
}
Controller
$categories = Category::orderBy('name', 'asc')->get();
return new CategoryCollection($categories);
I'm new with Laravel Resource so if someone have a path for solving this problem, thanks !
I solve my problem !
I create a relationship in my Category Model.
public function childrens()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
In my Category Resource
public function toArray($request)
{
return [
'slug' => $this->slug,
'name' => $this->name,
'childrens' => self::collection($this->whenLoaded('childrens'))
];
}
In my Controller
$categories = Category::with('childrens')->parents()->orderBy('name','asc')->get();

Laravel - pluck fields from relations

i'm new in laravel and php and now i wanna create a rest api
I have this query:
$questions = Question::with(
array('picture' => function($query){
$query>select('question_id','picture_name')>pluck('picture_name')>all();
},
'user'=>function($query){
$query->select('id','image','name');
}))->get()->toArray();
my query return this json:
{
"questions:": [
{
"id": 1,
"title": "23",
"caption": "last",
"address": "lsdbabfd",
"picture": [
{
"question_id": 1,
"picture_name": "1527484678.jpg"
},
{
"question_id": 1,
"picture_name": "1527485120.jpg"
}
],
"user": {
"id": 1,
"image": "defaultPicture",
"name": "alex"
}
}
]
}
but i want this json:
{
"questions:": [
{
"id": 1,
"title": "23",
"caption": "last",
"address": "lsdbabfd",
"picture": [
"1527484678.jpg",
"1527485120.jpg"
],
"user": {
"id": 1,
"image": "defaultPicture",
"name": "alex"
}
}
]
}
how can i do that?
i already tried to do that with Laravel pluck method and Eloquent Mutators
you can add attribute method
class Question extends Model
{
...
protected $appends = ['pictures'];
public function getPicturesAttribute()
{
return $this->pictures->pluck('picture_id');
}
}

Database structure for storing nested tags and categories

I'm working on a database that allows me to store nested tags and categories, and it is proving difficult to figure out a way that is relatively simple to query. I'm using Laravel, so a further barrier is making sure it is compatible with Eloquent in a reasonable way.
Currently, I have tried two ways.
The first way was to have a single table called entities, which has a type column, set to either tag or category. This seemed to work reasonably well with the data structure being used, but was slightly unpleasant to query.
The second way was to have two tables, one for each type. This was a complete nightmare, because then both tables needed to have two parent columns, and it became impossible to query with sane code.
So, I'm assuming the first way is the right way. Is there a sensible way to arrange the model and database migration? And how do I accurately query the database? This is my model (files refers to the file being tagged):
class Entity extends Model
{
public function children() {
return $this->hasMany('App\Entity');
}
public function parent() {
return $this->belongsTo('App\Entity');
}
public function files() {
return $this->belongsToMany('App\File')->withTimestamps();
}
}
And my migration (order is a column that ensures tags and categories are displayed in the correct positions, if siblings. It should also allow me to use JavaScript in the frontend to rearrange them, something I have not researched properly yet):
Schema::create('entities', function (Blueprint $table) {
$table->increments('id')->unsigned();
$table->integer('order');
$table->enum('type', ['tag', 'category']);
$table->string('name');
$table->string('title');
$table->text('description')->nullable();
$table->integer('entity_id')->unsigned()->nullable();
$table->timestamps();
$table->foreign('entity_id')->references('id')->on('entities');
});
This is a working example of the data structure being utilised by the frontend, which I'll need to generate from the database:
[
{
"__type": "Tag",
"id": "water",
"name": "Water",
"description": "Containing water of any kind.",
"checked": false,
"children": [
{
"__type": "Tag",
"id": "salt_water",
"name": "Salt Water",
"description": "Salt Water, for example in the Ocean.",
"checked": false,
"children": []
},
{
"__type": "Category",
"id": "fresh_water",
"name": "Fresh Water",
"description": "Fresh Water, such as rivers and lakes.",
"children": [
{
"__type": "Tag",
"id": "river",
"name": "River",
"description": "A river of water.",
"checked": true,
"children": []
},
{
"__type": "Tag",
"id": "lake",
"name": "Lake",
"description": "A lake of water.",
"checked": false,
"children": []
}
]
}
]
},
{
"__type": "Category",
"id": "species",
"name": "Species",
"description": "The Species of creature.",
"featured": true,
"children": [
{
"__type": "Tag",
"id": "dog",
"name": "Dog",
"description": "Canis Familiaris.",
"checked": false,
"children": []
},
{
"__type": "Tag",
"id": "cat",
"name": "Cat",
"description": "Felis Catus.",
"checked": false,
"children": [
{
"__type": "Tag",
"id": "tiger",
"name": "Tiger",
"description": "A tiger.",
"checked": false,
"children": []
}
]
}
]
}
]
if you have your data already in PHP array, you can use one of these recursive functions
function getChecked($root)
{
$output = [];
if(!is_array($root) && !isset($root['children']))
{
$root = array($root);
}
foreach($root as $item)
{
if(isset($item['checked']) && $item['checked']) {
if(isset($item['children']))
{
$children = getChecked($item['children']);
$item['children'] = $children;
}
$output[] = $item;
}
}
return $output;
}
function getChecked_lazy($root)
{
$output = [];
if(!is_array($root) && !isset($root['children']))
{
$root = array($root);
}
foreach($root as $item)
{
if(isset($item['children']))
{
$children = getChecked_lazy($item['children']);
$item['children'] = $children;
}
if( (isset($item['checked']) && $item['checked']) || isset($item['children']) && !empty($item['children']))
{
$output[] = $item;
}
}
return $output;
}
$data = [
[
"id" => "dog",
"checked" => true,
"children" => [
[
"id" => "wolf",
"checked" => true,
],
[
"id" => "fox",
"checked" => false
],
]
],
[
"id" => "cat",
"checked" => false,
"children" => [
[
"id" => "lion",
"checked" => true
],
],
],
];
echo json_encode(getChecked($data), JSON_PRETTY_PRINT);
echo "\n";
echo json_encode(getChecked_lazy($data), JSON_PRETTY_PRINT);
Output:
[
{
"id": "dog",
"checked": true,
"children": [
{
"id": "wolf",
"checked": true
}
]
}
]
[
{
"id": "dog",
"checked": true,
"children": [
{
"id": "wolf",
"checked": true
}
]
},
{
"id": "cat",
"checked": false,
"children": [
{
"id": "lion",
"checked": true
}
]
}
]
Or you can query by SQL where checked = true and then try to find the root (where parent is null).
Or other way

Categories