Fork or Download this repository and implement the logic to manage a menu.
A Menu has a depth of N and maximum number of items per layer M. Consider N and M to be dynamic for bonus points.
It should be possible to manage the menu by sending API requests. Do not implement a frontend for this task.
Feel free to add comments or considerations when submitting the response at the end of the README
.
- Home
- Home sub1
- Home sub sub
- [N]
- Home sub sub
- Home sub2
- [M]
- Home sub1
- About
- [M]
Create a menu.
{
"field": "value"
}
{
"field": "value",
"max_depth": 5,
"max_children": 5
}
{
"field": "value"
}
{
"field": "value",
"max_depth": 5,
"max_children": 5
}
Get the menu.
{
"field": "value"
}
{
"field": "value",
"max_depth": 5,
"max_children": 5
}
Update the menu.
{
"field": "value"
}
{
"field": "value",
"max_depth": 5,
"max_children": 5
}
{
"field": "value"
}
{
"field": "value",
"max_depth": 5,
"max_children": 5
}
Delete the menu.
Create menu items.
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
Get all menu items.
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
Remove all menu items.
Create an item.
{
"field": "value"
}
{
"field": "value"
}
Get the item.
{
"field": "value"
}
Update the item.
{
"field": "value"
}
{
"field": "value"
}
Delete the item.
Create item's children.
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
Get all item's children.
[
{
"field": "value"
},
{
"field": "value"
}
]
[
{
"field": "value",
"children": [
{
"field": "value",
"children": []
},
{
"field": "value"
}
]
},
{
"field": "value"
}
]
Remove all children.
Get all menu items in a layer.
[
{
"field": "value"
},
{
"field": "value"
}
]
Remove a layer and relink layer + 1
with layer - 1
, to avoid dangling data.
Get depth of menu.
{
"depth": 5
}
- 10 vs 1.000.000 menu items - what would you do differently?
- Write documentation
- Use PhpCS | PhpCsFixer | PhpStan
- Use cache
- Use data structures
- Use docker
Task descriptions could be written in much more detail because quite many things are not clear, like
- What are development restrictions - anything is allowed or without 3rd party dependencies, etc.;
- Route requirements and output could be described in more detail, for example, at first I did not understand how POST /items route is different from POST /menus/{menu}/items;
- Layer is not clearly understandable without extra information;
- Also, difficult to understand if max_children is per layer or per parent item.
I managed to do most of the tasks written above.
- All routes (with "bonus" requirements) are developed but tested only by using the actual API calls and debugging;
- Created data factories and seeders;
- Used PhpCS and PhpCsFixer for PSR1 and PSR2 rules;
- Used Redis (PHPRedis) cache;
- Used data structures;
- Used docker.
API Authentication not included just because this is a prototype project.
Things that could be done better but were excluded because of time consumed for these tasks:
- Caching is added only to MenuController;
- Data gathering is not fully optimized. Should have used eager loading in few places but I have not tested if that would reduce SQL connections;
- Some validations could be optimized better with less SQL connections (have not tested - just thoughts);
- Tests should be written;
- Validations could be moved to separate files for cleaner and more structured code;
- Documention should be written.
Regarding the question "10 vs 1.000.000 menu items - what would you do differently?": few previously mentioned bullet-points related to optimization and caching would make the difference for higher load of storage.
Docker project structure (docker files can be found in docker.zip archive inside this GitHub repository):
- app
- Laravel project files (pull from this GitHub repository)
- nginx
- conf.d
- app.conf
- conf.d
- php
- local.ini
- docker-compose.yml
- Dockerfile
Commands necessary for project to work:
composer install
php artisan migrate:refresh --seed