I think you're pretty much where the structure is. Based on what I understand from your question (and if you use Laravel), I would approach it like this:
Database
Name: menus Columns: id | builder | created_at | updated_at | deleted_at --------------------------------------------------------------------- eg. 1 | NULL | 1985-10-26 01:20:00 | 1985-10-26 01:21:00 | NULL 2 | blog | 1985-10-26 01:20:00 | 1985-10-26 01:21:00 | NULL Name: links Columns: id | menu_id | type | resource_type | resource_id | url | created_at | updated_at | deleted_at ------------------------------------------------------------------------------------------------------------------------------- eg. 1 | 1 | url | NULL | NULL | http://example.com/blog/1 | 1985-10-26 01:20:00 | 1985-10-26 01:21:00 | NULL 1 | 2 | route | blog | 1 | NULL | 1985-10-26 01:20:00 | 1985-10-26 01:21:00 | NULL Name: meta (or options) Columns: id | resource_type | resource_id | name | value --------------------------------------------------- eg. 1 | menu | 1 | bg_color | 000000 2 | link | 1 | target | _blank
Using this table, you can store menus, links (or a route defined in the corresponding builder or URL), as well as meta / options data for menus or links.
Note. . If you want to reuse links in several menus, you can remove menu_id from the links table, add the menu_links table for ManyToMany the relationship between menus and links
Models
Simple enough, you need a model for each table, i.e. Menu , Link and Meta
Builders
In the app directory of your project, I would place the Builders folder, and possibly the MenuBuilders folder inside it. Using this structure, you should skip the space in each Builder class under App\Builders\MenuBuilders .
With each Builder class, you can either extend the main MenuBuilder class, or, depending on how much you change for each Builder , you could implement a interface . Here is a quick example using interface (if you are not familiar with interfaces, I would suggest looking at Laracasts PHP Bootcamp ):
MenuBuilderInterface.php
namespace App\Builders\MenuBuilders; interface MenuBuilderInterface{ public build($menu); }
BlogMenuBuilder.php
namespace App\Builders\MenuBuilders; class BlogMenuBuilder implements MenuBuilderInterface { public build($menu) {
If you do not want to create all the methods enclosed in the interface , then perhaps the extension of the main MenuBuilder class will work better for you, it really depends on how different you see the menu and whether you use it and can reuse many methods in the main MenuBuilder .
On practice
So how would you use the above setting? As you said in your question, each Menu defines its constructor in the database, so we could create a simple build function in our Menu model to call the correct Builder class:
menu.php
namespace App; use Illuminate\Database\Eloquent\Model; class Menu extends Model { /** * Get the user first name. * * @param string $value * @return string */ public function build() { // First we define our namespace and get the builder based on the menu data $namespace = 'App\\Builders\\MenuBuilders\\'; $builder = !empty($this->builder)? $namespace.ucfirst($this->builder).'MenuBuilder' : $namespace.'MenuBuilder'; // Then check the builder class defined definitely exists // If no, replace with main menu builder class $builder = class_exists($builder)? $builder : $namespace.'MenuBuilder'; // Lastly we call the build method on a new instance of our builder // passing in our menu instance return new $builder()->build($this); } }
Now all we need in our view is to call the build function in our menu instance:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Example Site</title> </head> <body> <div id="menu"> <?php {!! $menu->build() !!} </div> </body> </html>
There are always several different ways to structure these kinds of things. Inevitably, you will understand what is best for you, based on the specific requirements of each result that you are after, but I hope this helps!