So, my problem is that I need to build a tree with data from two tables.
I have the following tables:
Category:
| id | parent_id | name |
|----|-----------|----------------|
| 1 | null | Category 1 |
| 2 | 1 | Category 1.1 |
| 3 | 2 | Category 1.1.1 |
| 4 | null | Category 2 |
| 5 | 4 | Category 2.1 |
| 6 | null | Category 3 |
Layer:
| id | category_id | name |
|----|-------------|---------|
| 1 | 2 | Layer 1 |
| 2 | 2 | Layer 2 |
| 3 | 3 | Layer 3 |
| 4 | 4 | Layer 4 |
| 5 | 4 | Layer 5 |
| 6 | 5 | Layer 6 |
My Category model:
class Category extends Model
{
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function childrens()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
public function layers()
{
return $this->hasMany('App\Layer', 'category_id', 'id');
}
}
Layer model:
class Layer extends Model
{
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
}
I'm using the following function to build the category tree:
public function index()
{
$categories = Category::all();
$layers = Layer::all();
return $this->buildTree($categories->toArray(), null);
}
function buildTree($categories, $parent_id)
{
$categoriesTree = [];
foreach ($categories as $category) {
$category['folder'] = true;
if ($category['parent_id'] == $parent_id) {
$childrens = $this->buildTree($categories, $category['id']);
if ($childrens) {
$category['childrens'] = $childrens;
}
$categoriesTree[] = $category;
}
}
return $categoriesTree;
}
The above function works well for categories and the response is:
Category 1
Category 1.1
Category 1.1.1
Category 2
Category 2.1
Category 3
But I want to add layers as child of respective category, like the following:
Category 1
Category 1.1
Category 1.1.1
Layer 3
Layer 1
Layer 2
Category 2
Category 2.1
Layer 6
Layer 4
Layer 5
Category 3
What is the best way to do this?
I suggest using a relationship in your Category model with the Layer model and eager load it. This way you achieve the same result but with less overhead on your buildTree function because Laravel is doing most of the work:
Category.php model
class Category extends Model
{
// ...
public function layers()
{
return $this->hasMany(Layer::class);
}
// ...
}
In your controller:
public function index()
{
$categories = Category::with('layers')->get();
// ...
}
This results in an array like this:
Related
I wrote a recursive function in order to echo a nested list. In fact it's a helper function in laravel that get a collection of nested items and supposed to go through it and print all items' name.
But when I using it in view, just the first level of items is printed.
It seems to doesn't execute itself at items_list($item->children());.
function items_list($items)
{
foreach($items as $item) {
if(!$item->hasChild())
{
echo "
<li>
".$item->id.".".$item->name."
</li>
";
return null;
}
else
{
echo "<li>".
$item->id.".".$item->name
."<ul>";
items_list($item->children());
echo "</ul></li>";
}
}
}
here is the collection of items:
| id | parent_id | name |
+----+-----------+---------------------+
| 1 | NULL | Dr. Shany Braun Jr. |
| 2 | NULL | Lily Jerde |
| 3 | NULL | Dr. Herbert Kunze |
| 4 | 1 | Ms. Shanie Skiles |
| 5 | 1 | Dr. Anissa Kunze |
| 6 | 2 | Maybelle Sawayn |
| 7 | 3 | Cassie Trantow Jr. |
| 8 | 5 | Gussie Smith |
| 9 | 5 | Carol Mueller |
| 10 | 7 | Shany Bayer |
+----+-----------+---------------------+
edit:
The input of function is in fact a collection of an eloquent model that has retrieved from database I have printed above.
And here is the model:
class Person extends Model
{
protected $table = 'people';
function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
function children()
{
return $this->hasMany(self::class,'parent_id');
}
static function first_level()
{
return self::all()->where('parent_id', NULL);
}
function hasChild()
{
return ($this->children() ? TRUE : FALSE);
}
}
I have tested each function of this class and they work ‌properly.
and here is controller:
class HomeController extends Controller
{
public function show(){
$people = Person::first_level();
items_list($people);
}
}
and this is the output:
1.Dr. Shany Braun Jr.
2.Lily Jerde
3.Dr. Herbert Kunze
I got 4 tables:
// Table countries
+----+------+
| Id | Name |
+----+------+
| 1 | USA |
| 2 | GB |
+----+------+
// Table platforms
+----+---------+
| Id | Name |
+----+---------+
| 1 | Windows |
| 2 | Linux |
+----+---------+
// Table users
+----+-------+------------+-------------+
| Id | Name | country_id | platform_id |
+----+-------+------------+-------------+
| 1 | Admin | 1 | 1 |
| 2 | Test | 2 | 1 |
+----+-------+------------+-------------+
// Table posts
+----+-----------+------------+-------------+---------+
| Id | Title | country_id | platform_id | user_id |
+----+-----------+------------+-------------+---------+
| 1 | TestPost1 | 2 | 1 | 1 |
| 2 | TestPost2 | 2 | 2 | null |
+----+-----------+------------+-------------+---------+
The database should be able to implement the following relations:
User (N) <-> (N) Platform
User (N) <-> (N) Country
User (0..1) <-> (N) Post
Post (N) <-> (N) Country
Post (N) <-> (1) Platform
So now I tried to implement these relations following Laravel Eloquent ORM documentation:
// Country.php
public function posts()
{
return $this->belongsToMany('App\Post');
}
public function users()
{
return $this->belongsToMany('App\User');
}
// Platform.php
public function users()
{
return $this->belongsToMany('App\User');
}
public function posts()
{
return $this->belongsToMany('App\Post');
}
// User.php
public function posts()
{
return $this->hasMany('App\Post');
}
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Platform');
}
// Post.php
public function countries()
{
return $this->hasMany('App\Country');
}
public function platforms()
{
return $this->hasMany('App\Comment');
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
But now I am confused, as I thought the way to implement N to N relations in mysql is to add a third table to db, for example like that:
// Table CountryUserRelations to implement User (N) <-> (N) Country
+----+------------+---------+
| Id | country_id | user_id |
+----+------------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 2 |
+----+------------+---------+
But how does Eloquent ORM handle the rules inside my model? Will it keep the N to N relations without having to add a relations table? Or am I missing something or misunderstanding the Eloquent ORM Relations concept?
I just joined stackoverflow so I do not have enough credit to comment so I will leave an asnwer here.
First of all please correct your relationship definition.
in User Model:( you have mistake here)
public function countries(){
return $this->belongsToMany(Country::class);
}
and in your Country Model:
public function users(){
return $this->belongsToMany(User::class);
}
second you need to create country_user table using:
php artisan make:migration create_country_user_table
after it you need to complete your table:
Schema::create('country_user', function (Blueprint $table){
$table->increments('id');
$table->unsignedInteger('country_id');
$table->unsignedInteger('user_id');
$table->foreign('country_id')->references('id')->on('countries');
$table->foreign('user_id')->references('id')->on('users');
}
I'm trying Eloquent now with high expectations.
I have a category tree.
Everything works fine. But now I want to return the whole tree as json. Therefore I'm doing:
$categories = Category::whereNull('parent_id')->get()->toJson();
And only get the adam and eve nodes.
[{"category_id":1,"name":"Boats","slug":"boats","parent_id":null},
{"category_id":2,"name":"Paddles","slug":"paddles","parent_id":null}]
Which is good basically. How to integrate the childs recursivly? No "native" eloquent way for that?
Tree like this:
select * from categories;
+-------------+----------------+----------------+-----------+
| category_id | name | slug | parent_id |
+-------------+----------------+----------------+-----------+
| 1 | Boats | boats | NULL |
| 2 | Paddles | paddles | NULL |
| 3 | Kayaks | kayaks | 1 |
| 4 | Canoes | canoes | 1 |
| 5 | Carbon Paddles | carbon-paddles | 2 |
| 6 | Vajda K1 | vajda-k1 | 4 |
| 7 | Dagger RPM | dagger-rpm | 3 |
| 8 | Kober Viper | vober-viper | 2 |
+-------------+----------------+----------------+-----------+
8 rows in set (0.03 sec)
and a model like that
class Category extends Eloquent {
protected $table = 'categories';
protected $primaryKey = 'category_id';
protected $fillable = array("name", "slug", "parent_id");
public $timestamps = FALSE;
// each category has many subcategories
public function childs() {
return $this->hasMany('Category');
}
// each category belogs to one parent category
public function parent() {
return $this->belongsTo('Category');
}
}
AS far as I know there is no native way to get a recursive result from eloquent directly.
For the first level you would use:
$categories = Category::whereNull('parent_id')->with('childs')->get()->toJson();
For the next level (and likewise further on):
$categories = Category::whereNull('parent_id')->with(['childs' => function ($query) {
$query->with('childs');
}])->get()->toJson();
Using Lazy Eager Loading you'll be able to build your own PHP giveMeMyCatTree() methode.
Does this help?
I trying to create a search function in php with Slim and Eloquent 5.1, but i have a problem to get the results from the database.
This are my tables
Listing Table
id | title | description | price | size |etc...
-----|--------|-------------|-------|--------|-------
1 | list1 | some text | 500 | 50 |
2 | list2 | some text | 700 | 80 |
3 | list3 | some text | 350 | 120 |
Listings Option Table
id | id_listing | opt_name | opt_value
-----|-------------|-------------|----------
1 | 1 | rooms | 3
2 | 1 | baths | 4
3 | 2 | rooms | 8
4 | 3 | baths | 1
5 | 3 | rooms | 6
Listings Images Table
id | id_listing | file_name
-----|-------------|-------------
1 | 1 | file_1.png
2 | 1 | file_2.png
1 | 2 | file_3.png
2 | 3 | file_4.png
Now i found a way to get all the results with all they options with
listado = $app->listing->with(['options'],['imgs'])->get();
My classes
//Listing Class
class Listing extends Eloquent{
protected $table = 'listings';
public function options(){
return $this->hasMany('Casas\Listing\ListingOption', 'id_listing', 'id');
}
public function imgs(){
return $this->hasMany('Casas\Listing\ListingImg', 'id_listing');
}
}
// ListingOption class
class ListingOption extends Eloquent{
protected $table = 'listings_options';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
// ListingImg Class
class ListingImg extends Eloquent{
protected $table = 'listings_imgs';
public function listings()
{
return $this->belongsTo('Casas\Listing\Listing', 'id_listing');
}
}
but i cannot find a way to filter the query the way i want to. I tried many thing.
Example: I want to get all the listings that have price less than 600 and have more than 2 rooms.
So that be listing id 1 and 3
If anyone can help create this query i'll appreciate that.
What you want want to use is whereHas.
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship
$result = $app->listing->with('options', 'imgs')
->whereHas('options', function ($query) {
$query->where('opt_value', '>', 2)
->where('opt_name', 'room');
})
->where('price', '<', 600)->get();
So what we are doing here is, eager loading options and imgs tables and fetching only the listings that have at least one option where opt_value is bigger than two and opt_name is room.
You can find more in info about querying relationships in here
I would try something like this:
$result = $app->listing->with(
['options' => function ($query) {
$query->where('opt_value', '>', 2);
}
],
['imgs'])->where('price', '<', 600)->get();
The last, outer where should be assigned to listing. The inner where, passed in a callback, should be assigned to options directly.
Take a look at the docs (the "Constraining Eager Loads" section).
I have 3 models: Basket, Product and Asset.
A Basket can contain many Assets, and one asset can appear in multiple Baskets.
A Product can have many Assets, but an Asset can also be assigned to more than one Product.
The aim is to get Assets into a Basket while still knowing which Product it was added from.
The table structure I envision having is this:
-------------------------------------
| basket_id | asset_id | product_id |
-------------------------------------
| 1 | 1 | 1 |
| 1 | 3 | 1 |
| 1 | 15 | 2 |
| 2 | 23 | 1 |
| 2 | 3 | 1 |
| 3 | 79 | 3 |
-------------------------------------
I also envision a model setup of something like this:
class Basket extends Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
class Product extends Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
class Asset extends Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
public function product() {
// This should return the product from the product_id column
}
}
I'm not sure how to go about writing Asset::product() so that it returns the corresponding Product.
I'd like to call something like Basket::find(1)->assets()->first()->product; to get the product that the first asset was added to the basket from.
Here's a small piece that does what you need
https://github.com/jarektkaczyk/Eloquent-triple-pivot
and thanks to your PR a package on packagist:
https://packagist.org/packages/jarektkaczyk/eloquent-triple-pivot