Skip to content

butschster/dbml-parser

Repository files navigation

DBML parser written on PHP8

Support me on Patreon Latest Stable Version Build Status Total Downloads License

DBML-parser

DBML (Database Markup Language) is a domain-specific language (DSL) designed to define database structures in a simple and readable format.

Note

This page has all the info on how it works. Check out https://www.dbml.org for more info.

We were inspired by the cool service at https://dbdiagram.io/ to create this parser. It's super useful for visualizing databases and we thought it'd be great to use it to build schema and then turn it into an object tree for generating things like CycleORM entity schemas, Laravel models, and migrations.

To parse DBML, we used a handy PHP package called https://phplrt.org.

Features

  • DBML parser

Requirements

  • PHP 8.0 and above

Quick start

To install the package, run the following command from the root directory of your project:

composer require butschster/dbml-parser

That's it!

Usage

use Butschster\Dbml\DbmlParserFactory;

$parser = DbmlParserFactory::create();

$schema = $parser->parse(<<<DBML
    Project test {
        database_type: 'PostgreSQL'
        Note: 'Description of the project'
    }
    
    //// -- LEVEL 1
    //// -- Tables and References
    
    // Creating tables
    Table users as U {
        id int [pk, unique, increment] // auto-increment
        full_name varchar [not null, unique, default: 1]
        created_at timestamp
        country_code int
        type int
        note int
        Note: 'khong hieu duoc'
    }
    
    Table merchants {
        id int
    }
    
    Table countries {
        code int [pk]
        name varchar
        continent_name varchar
    }
    
    // Creating references
    // You can also define relationship separately
    // > many-to-one; < one-to-many; - one-to-one
    Ref{
        U.country_code > countries.code
        merchants.country_code > countries.code
    }
    
    //----------------------------------------------//
    
    //// -- LEVEL 2
    //// -- Adding column settings
    
    Table order_items {
        order_id int [ref: > orders.id]
        product_id int
        quantity int [default: 1] // default value
    }
    
    Ref: order_items.product_id > products.id
    
    Table orders {
        id int [pk] // primary key
        user_id int [not null, unique]
        status varchar
        created_at varchar [note: '''When order created'''] // add column note
    }
    
    Table int {
        id int
    }
    
    //----------------------------------------------//
    
    //// -- Level 3
    //// -- Enum, Indexes
    
    // Enum for 'products' table below
    Enum products_status {
        out_of_stock
        in_stock
        running_low [note: 'less than 20'] // add column note
    }
    
    // Indexes: You can define a single or multi-column index
    Table products {
        id int [pk]
        name varchar
        merchant_id int [not null]
        price int
        status products_status
        created_at datetime [default: `now()`]
    
        Indexes {
            (merchant_id, status) [name:'product_status', type: hash]
            id [unique]
        }
    }
    
    Ref: products.merchant_id > merchants.id // many-to-one
    
    TableGroup hello_world {
        just_test
        just_a_test
    }

    DBML
);

Schema data

// List of tables
$tables = $schema->getTables(); // \Butschster\Dbml\Ast\TableNode[]

// Check if table exists
$schema->hasTable('users');

// Get table by name
$table = $schema->getTable('users'); // \Butschster\Dbml\Ast\TableNode[]

// Get project data
$project = $schema->getProject(); // \Butschster\Dbml\Ast\ProjectNode

// Get table groups
$tableGroups = $schema->getTableGroups(); // \Butschster\Dbml\Ast\TableGroupNode[]

// Check if table group with given name exists
$schema->hasTableGroup('name');

// Get table group object by name
$tableGroup = $schema->getTableGroup('name'); // \Butschster\Dbml\Ast\TableGroupNode

// Get enums
$enums = $schema->getEnums(); // \Butschster\Dbml\Ast\EnumNode[]

// Check if enum with given name exists
$schema->hasEnum('name');

// Get enum object by name
$enum = $schema->getEnum('name'); // \Butschster\Dbml\Ast\EnumNode

// Get refs
$refs = $schema->getRefs(); // \Butschster\Dbml\Ast\RefNode[]

Project data

Project test {
    database_type: 'PostgreSQL'
    Note: 'Description of the project'
}
/** @var \Butschster\Dbml\Ast\ProjectNode $project */
$project = $schema->getProject();
$name = $project->getName(); // test
$note = $project->getNote(); // Description of the project

/** @var \Butschster\Dbml\Ast\Project\SettingNode $setting */
$setting = $project->getSetting('database_type');
$databaseType = $setting->getValue(); // PostgreSQL
$key = $setting->getKey(); // database_type

Table data

Table users as U {
    id int [pk, unique, increment] // auto-increment
    full_name varchar [not null, unique, default: 1]
    created_at timestamp
    country_code int
    type int
    note int
    Note: 'khong hieu duoc'
}
/** @var \Butschster\Dbml\Ast\TableNode $table */
$table = $schema->getTable('users');

$name = $table->getName(); // users
$alias = $table->getAlias(); // U

$note = $table->getNote(); // khong hieu duoc

// Get table columns
$columns = $table->getColumns(); // \Butschster\Dbml\Ast\Table\ColumnNode[]

// Check if table column exists
$table->hasColumn('id');

// Get column by name
$column = $table->getColumn('id');

// Get table indexes
$indexes = $table->getIndexes(); // \Butschster\Dbml\Ast\Table\IndexNode[]

Table column data

Table users as U {
    id int [pk, unique, increment] // auto-increment
}
/** @var \Butschster\Dbml\Ast\Table\ColumnNode $column */
$column = $schema->getTable('users')->getColumn('id');

$name = $column->getName(); // id
$type = $column->getType()->getName(); // int
$size = $column->getType()->getSize(); // null|int

$note = $column->getNote(); // string

$refs = $column->getRefs(); // \Butschster\Dbml\Ast\RefNode[]

/** @var \Butschster\Dbml\Ast\Values\IntNode $default */
$default = $column->getDefault(); 
$value = $default->getValue(); // 1

// Check if column is primary
$column->isPrimaryKey();

// Check if column is auto increment
$column->isIncrement();

// Check if column is unique
$column->isUnique();

// heck if column is nullable
$column->isNull();

Table index data

Table products {
    id int [pk]
    name varchar
    merchant_id int [not null]
    price int
    status products_status
    created_at datetime [default: `now()`]
    
    Indexes {
        (merchant_id, status) [name:'product_status', type: hash]
        id [unique]
    }
}
/** @var \Butschster\Dbml\Ast\Table\IndexNode $index */
$index = $schema->getTable('products')->getIndexes()[0];

/** @var \Butschster\Dbml\Ast\Values\StringNode[]|\Butschster\Dbml\Ast\Values\ExpressionNode[] $columns */
$columns = $index->getColumns();

count($columns); // 2

$column1 = $index->getColumns()[0]->getValue(); // merchant_id
$column2 = $index->getColumns()[1]->getValue(); // status

$type = $index->getType(); // hash
$name = $index->getName(); // product_status

$note = $index->getNote();

// Check if index is pk
$index->isPrimaryKey(); 

// Check if index is unique
$index->isUnique(); 

Enum data

Enum products_status {
  out_of_stock
  in_stock
  running_low [note: 'less than 20'] // add column note
}
/** @var \Butschster\Dbml\Ast\EnumNode $enum */
$enum = $schema->getEnum('products_status');

$name = $enum->getName(); // products_status

// Get amount of values
$enum->count(); // 3

// Check if enum contains value
$enum->hasValue('out_of_stock'); // true

// Get enum value object by name
$value = $enum->getValue('running_low'); //  \Butschster\Dbml\Ast\Enum\ValueNode

$note = $value->getNote();
$value = $value->getValue();

Table group data

TableGroup hello_world {
    just_test
    just_a_test
}
/** @var \Butschster\Dbml\Ast\TableGroupNode $group */
$group = $schema->getTableGroup('hello_world');

$name = $group->getName(); // hello_world

// Check if table with given name contains in this group
$group->hasTable('just_test');

// Get list of tables
$tables = $group->getTables(); // string[]

Ref data

Ref optional_name: products.merchant_id > merchants.(id, name) [delete: cascade, update: no action]
/** @var \Butschster\Dbml\Ast\RefNode $ref */
$ref = $schema->getRefs()[0];

$name = $ref->getName(); // optional_name

$type = $ref->getType(); // \Butschster\Dbml\Ast\Ref\Type\ManyToOneNode

$leftTable = $ref->getLeftTable(); // \Butschster\Dbml\Ast\Ref\LeftTableNode
$table = $leftTable->getTable(); // products
$columns = $leftTable->getColumns(); // ['merchant_id']

$rightTable = $ref->getRightTable(); // \Butschster\Dbml\Ast\Ref\RightTableNode
$table = $rightTable->getTable(); // merchants
$columns = $rightTable->getColumns(); // ['id', 'name']

$onDelete = $ref->getAction('delete');
$action = $onDelete->getAction(); // cascade

$onUpdate = $ref->getAction('update');
$action = $onUpdate->getAction(); // no action

Enjoy!