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
.
Fork this repository and return a public git link, to present the task.
Git history will be reviewed aswell as part of this task.
If you don't have time to implement a part of the system but you know how, try create the placeholder and write comments with the intended behaviour.
- 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
- Implement tests
- Clone directory from git
- Create a .env file and copy database credentials from docker-compose.yml
- Run
docker-compose up
- After creating containers run
docker-compose exec app composer install
- Run
docker-compose exec app composer require predis/predis
- After installing composer run
docker-compose exec app php artisan migrate
- Go to
localhost:8080
in your browser
- Install composer
- Create a .env file from .envexample
- Run
composer install
- Run
composer require predis/predis
- Run
php artisan key:generate
- Put database credentials
- Run
php artisan migrate
- Run
php artisan serve
- Go to
localhost:8000
in your browser
POST /api/menus
{
"name": "Home",
"max_depth": 5,
"max_children": 3
}
GET /menus/{menu}
api/menus/1
PUT|PATCH /menus/{menu}
|
api/menus/1
{
"name": "Home 2",
"max_depth": 6,
"max_children": 7
}
DELETE /menus/{menu}
api/menus/1
POST /menus/{menu}/items
|
api/menus/1/items
{
"name": "Menu Item"
}
GET /menus/{menu}/items
| api/menus/1/items
DELETE /menus/{menu}/items
| api/menus/1/items
POST api/items
{
"name": "Item 1",
"menu_id": 1,
"item_id": 1
}
GET /items/{item}
| api/items/1
PUT|PATCH /items/{item}
| api/items/1
DELETE /items/{item}
| api/items/1
POST /items/{item}/children
| /api/1/children
{
"name": "Item Sub"
"item_id": 2
}
GET /items/{item}/children
| api/items/2/children
{
"name": "Item Sub"
"item_id": 2
}
DELETE /items/{item}/children
| api/items/2/children
GET /menus/{menu}/layers/{layer}
| /menus/1/layers/1
GET /menus/{menu}/depth
| api/1/depth
Note: If testing the api via Postman please add this script that generates CSRF Token , to Pre-Request Script
console.log('Pre-request Script from Request start');
// We don't need to do anything if it's GET or x-csrf-token header is explicitly presented
if (pm.request.method !== 'GET' && !(pm.request.headers.has('x-csrf-token'))) {
var csrfRequest = pm.request.clone();
csrfRequest.method = 'GET';
if (pm.request.method === 'POST') {
// for POST method usually it is ....<something>Collection in the URL
// so we add $top=1 just to quickly get csrf token;
// for PUT, PATCH or DELETE the same URL would be enough,
// because it points to the actual entity
csrfRequest.url = pm.request.url + '?$top=1';
}
csrfRequest.upsertHeader({
key: 'x-csrf-token',
value: 'fetch'
});
pm.sendRequest(csrfRequest, function(err, res) {
console.log('pm.sendRequest start');
if (err) {
console.log(err);
} else {
var csrfToken = res.headers.get('x-csrf-token');
if (csrfToken) {
console.log('csrfToken fetched:' + csrfToken);
pm.request.headers.upsert({
key: 'x-csrf-token',
value: csrfToken
});
} else {
console.log('No csrf token fetched');
}
}
console.log('pm.sendRequest end');
});
}
console.log('Pre-request Script from Request end');
- I would index the database , and test the performance . If still facing issues I would consider maybe no sql , or mongodb for this part.
tests/Feature/ExampleTest.php