db-to-php: The new “intelligent” PHP database framework
Contents
What is exactly db-to-php
Before I answer that question, let’s look shortly at the different types of database frameworks.
The database frameworks are divided in two main categories:
- ORM (object relational mapping), ex: Doctrine
- Active Record, ex: Eloquent by Laravel
db-to-php was created with the intention of being in neither of them, but just as a tool which can be incrementally integrated to represent database tables in code, giving the user the full flexibility on decisions such as how to connect to the database, how to write, save etc.
This changed with the new feature branch, in which I decided to start offering ORM functionalities. There is an Entity Manager that will be offered out of the box, and entities will also track the state of the changes.
That means that you will be able to save, delete, update entities very easily like in any other ORM, and also updates will be very fast due to updating only the changed fields. (Dirty fields are tracked in entities).
So what does db-to-php does exactly and why should you use it ?
db-to-php does:
- Generate entities for each table specified.
- Generate entity factories for each table specified.
- A basic Entity Manager (started implementing the ORM pattern in the feature branch). Entities track changes, and can be saved and updated. During updates, only the changed fields are updated.
- Use the generated factories for unit tests and seeding dummy data.
When should you use it?
You can use it in any project as long as it is well defined. This tool is a must for projects that need high quality, proper testing, and reduce the level of possible errors.
Type hinting, strongly formalizing database tables into classes are the strongest points against other frameworks like for example Eloquent.
In Eloquent nothing would prevent a developer from trying to access a non-existing column in a database. The fields are accessed dynamically, and no one knows what fields are available.
In db-to-php, the editor of choice would suggest properly the available fields, as they are methods or properties (depending on your settings) of the generated class.
Why I created db-to-php
While there are many PHP database frameworks out there, I found none of them to have the following functionalities:
- automatically generates the corresponding entity classes based on a table schema
- determine the field types correctly
- generate factories, which can create random data correctly depending on the type for each table column, and objects of the generated entities
And then I decided to create db-to-php.
If you come from a .NET background, db-to-php does what LINQ TO SQL does, aside from the active record functionality.
Example of entity creation
Let’s see a simple example of entity generation. Suppose we have the table with the following MySQL definition:
1 2 3 4 5 6 7 8 9 10 11 12 |
CREATE TABLE users_demo ( id BIGINT(20) PRIMARY KEY NOT NULL AUTO_INCREMENT, name VARCHAR(20) NOT NULL, surname VARCHAR(20) NOT NULL, preferences JSON, birth_year YEAR(4) NOT NULL, nr_cars TINYINT(3) unsigned NOT NULL, salary DECIMAL(18,4) NOT NULL, active TINYINT(1) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ); |
After running the command
1 |
vendor/bin/dbToPhp generate:entities |
the following entity is generated at the location specified in the config file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
<?php namespace Entities; class UsersDemoEntity { /** * @var int */ private $id; /** * @var string */ private $name; /** * @var string */ private $surname; /** * @var string|null */ private $preferences; /** * @var int */ private $birthYear; /** * @var int */ private $nrCars; /** * @var float */ private $salary; /** * @var bool */ private $active; /** * @var string */ private $createdAt; /** * @param int $id * @return $this */ public function setId(int $id) { $this->id = $id; return $this; } /** * @return int */ public function getId() : int { return $this->id; } /** * @param string $name * @return $this */ public function setName(string $name) { $this->name = $name; return $this; } /** * @return string */ public function getName() : string { return $this->name; } /** * @param string $surname * @return $this */ public function setSurname(string $surname) { $this->surname = $surname; return $this; } /** * @return string */ public function getSurname() : string { return $this->surname; } /** * @param string|null $preferences * @return $this */ public function setPreferences(?string $preferences) { $this->preferences = $preferences; return $this; } /** * @return string|null */ public function getPreferences() : ?string { return $this->preferences; } /** * @param int $birthYear * @return $this */ public function setBirthYear(int $birthYear) { $this->birthYear = $birthYear; return $this; } /** * @return int */ public function getBirthYear() : int { return $this->birthYear; } /** * @param int $nrCars * @return $this */ public function setNrCars(int $nrCars) { $this->nrCars = $nrCars; return $this; } /** * @return int */ public function getNrCars() : int { return $this->nrCars; } /** * @param float $salary * @return $this */ public function setSalary(float $salary) { $this->salary = $salary; return $this; } /** * @return float */ public function getSalary() : float { return $this->salary; } /** * @param bool $active * @return $this */ public function setActive(bool $active) { $this->active = $active; return $this; } /** * @return bool */ public function getActive() : bool { return $this->active; } /** * @param string $createdAt * @return $this */ public function setCreatedAt(string $createdAt) { $this->createdAt = $createdAt; return $this; } /** * @return string */ public function getCreatedAt() : string { return $this->createdAt; } } |
As can be seen above, the field types are detected, type hints are used, and annotations are filled properly. The settings can be changed in the configuration files according to your generation preferences.
A full manual is located at the Github page of the library, at db-to-php.
Example of entity factory creation
Let’s see another example, in which we generate an entity factory.
The table schema is the same as in the example of entity generation.
After running
1 |
vendor/bin/dbToPhp generate:factories |
the following entity factory is generated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?php namespace Factories\Entities; use Entities\UsersDemoEntity; class UsersDemoEntityFactory extends AbstractEntityFactory { /** * @param array $data * @return UsersDemoEntity */ public static function make(array $data = []) : UsersDemoEntity { return self::makeFromData(self::makeData($data)); } /** * @param array $data * @return UsersDemoEntity */ public static function makeFromData(array $data) : UsersDemoEntity { return self::mapArrayToEntity($data, UsersDemoEntity::class); } /** * @param array $data * @return array */ public static function makeData(array $data = []) : array { return [ 'id' => $data['id'] ?? self::randomInt64(), 'name' => $data['name'] ?? self::randomString(rand(0, 20)), 'surname' => $data['surname'] ?? self::randomString(rand(0, 20)), 'preferences' => $data['preferences'] ?? self::randomJson(), 'birth_year' => $data['birth_year'] ?? self::randomYear(4), 'nr_cars' => $data['nr_cars'] ?? self::randomUnsignedInt8(), 'salary' => $data['salary'] ?? self::randomFloat(4), 'active' => $data['active'] ?? self::randomBoolean(), 'created_at' => $data['created_at'] ?? self::randomDate('Y-m-d H:i:s'), ]; } } |
The generated data respects strictly the field types of the database table schema.
Not only the types, but also the lengths (int4, int8, etc) and special types are detected (year, date, etc).
The common use cases of entity factories are:
- Used for simplifying unit tests. Instead of mocking any method of your entities, you can auto generate an entity via the static make method, and override only the necessary fields.
- Can be used for generating seed data and populating a database with articles, posts etc.
For a full documentation on how to install and use the framework, check its Github page: db-to-php.
Thank you for reading. I would like you to leave your comments below with suggestions, critics and any insight you might have.
And of course, I would be glad if you would want to contribute.