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
The task has taken more time than I expected, mostly because I'm not used to Laravel's and Eloquent's quirks and conventions and I sometimes had to work around them. The code probably needs some slight refactoring to make it perfect, because is not ideal as of now.
They exist mostly because of the time constraints. For a proper project I would have ironed them all out, of course.
- The tests use production database
- There are some missing feature tests, I have decided to give them up for time constraints
- There are no unit tests for the service classes
- There are some quirks in the code that probably need refactoring:
- The
MenuRegistry
service class is basically a mediator pattern implementation, and it violates the single responsibility principle. Can be reworked into dedicated classes. - Some database operations could use transactions to be atomic
- Complex store request validation should be refactored into custom rule objects
- The container structure should use a dedicated server container like nginx, not the php development server
- Php 7.4 can be used instead of 7.3
- Instead of using the simple database tree implementation, a dedicated nested set can be used for future development
- Cache isn't used for database requests, I ran out of time.
- The
- Ultra simple Makefile
- No CI, phpcs and such must be run manually
After cloning run:
make init
To start the HTTP server:
make up
To run the tests
make test