経緯
ServiceとRepository間でデータのやり取りをする際に、型付きのオブジェクトを使うため、SymfonyのORMライブラリdoctrineを基にしたlaravelのライブラリである「laravel-doctrine/orm」を使用して、DBのスキーマから直接クラスをgetter, setter付きで自動生成する、というのを試してみました。
前提条件
Laravel5.8
laravel-doctrine/orm 1.4
MySQL8.*
laravel-doctrine/ormは開発で自動コード生成にのみ使用します。実際のDB操作には使用しない想定です。
1 laravel-doctrineの取得
composer require laravel-doctrine/orm --dev
2 config/doctrine.phpの生成、修正
php artisan vendor:publish --tag="config"
[出力]
Copied File [/vendor/laravel-doctrine/orm/config/doctrine.php] To [/config/doctrine.php]
Publishing complete.
下記のようにconfig/doctrine.phpを修正します。
'managers' => [
'default' => [
'dev' => env('APP_DEBUG', false),
'meta' => env('DOCTRINE_METADATA', 'annotations'),
'connection' => env('DB_CONNECTION', 'mysql'),
'namespaces' => ['App\Mappings'],
'paths' => [
base_path('app/Mappings')
],
'repository' => Doctrine\ORM\EntityRepository::class,
'proxies' => [
'namespace' => false,
'path' => storage_path('proxies'),
'auto_generate' => env('DOCTRINE_PROXY_AUTOGENERATE', false)
],
3 PrimaryKeyがないテーブルに対するエラー の回避
TableにPrimaryKeyが無いと、次のコマンドでDatabaseからマッピングのクラスをインポートする際に、下記のエラー が出る。
Doctrine\ORM\Mapping\MappingException : Table change_reason_master has no primary key. Doctrine does not support reverse engineering from tables that don't have a primary key.
これを避けるため、vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\Driver\DatabaseDriver.php
を置き換えると自動生成が動くようでした。
(コードを生成後は元のコードに戻しておく、との記述がありました)
//~~~~
if ( ! $table->hasPrimaryKey()) {
// throw new MappingException(
// "Table " . $table->getName() . " has no primary key. Doctrine does not ".
// "support reverse engineering from tables that don't have a primary key."
// );
}
// $pkColumns = $table->getPrimaryKey()->getColumns();
//ADD ↓↓↓
$pkColumns = $table->hasPrimaryKey()? $table->getPrimaryKey()->getColumns() : [];
//ADD ↑↑↑
//~~~~
private function getTablePrimaryKeys(Table $table)
{
try {
//DELETE ↓↓↓
// return $table->getPrimaryKey()->getColumns();
//DELETE ↑↑↑
//ADD ↓↓↓
if($table->hasPrimaryKey()){
return $table->getPrimaryKey()->getColumns();
}
//ADD ↑↑↑
} catch (SchemaException $e) {
// Do nothing
}
return [];
}
4 マッピングの作成
php artisan doctrine:mapping:import annotation
→app\Mapping直下にテーブルのEntityの元データ(getter, setterのメソッド無し)が作成される。
(これでもクラスとしては使用できるが全てprivate のプロパティのみのクラスとなってしまう。)
5 getter, setter付きクラスの生成
php artisan doctrine:generate:entities --generate-annotations --generate-methods
→app\Entities直下にテーブルのEntityの元データ(getter, setterのメソッド付き)が作成される
(app\Mappingにあるphpファイルを元に生成される)
生成されたUsersクラス
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* @ORM\Table(name="users")
* @ORM\Entity
*/
class Users
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, options={"unsigned"=true}, unique=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=50, precision=0, scale=0, nullable=false, unique=false)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=50, precision=0, scale=0, nullable=false, unique=false)
*/
private $email;
/**
* @var \DateTime|null
*
* @ORM\Column(name="email_verified_at", type="datetime", precision=0, scale=0, nullable=true, unique=false)
*/
private $emailVerifiedAt;
/**
* @var string|null
*
* @ORM\Column(name="password", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
*/
private $password;
/**
* @var string|null
*
* @ORM\Column(name="remember_token", type="string", length=100, precision=0, scale=0, nullable=true, unique=false)
*/
private $rememberToken;
/**
* @var \DateTime|null
*
* @ORM\Column(name="created_at", type="datetime", precision=0, scale=0, nullable=true, unique=false)
*/
private $createdAt;
/**
* @var \DateTime|null
*
* @ORM\Column(name="updated_at", type="datetime", precision=0, scale=0, nullable=true, unique=false)
*/
private $updatedAt;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name.
*
* @param string $name
*
* @return Users
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set email.
*
* @param string $email
*
* @return Users
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email.
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set emailVerifiedAt.
*
* @param \DateTime|null $emailVerifiedAt
*
* @return Users
*/
public function setEmailVerifiedAt($emailVerifiedAt = null)
{
$this->emailVerifiedAt = $emailVerifiedAt;
return $this;
}
/**
* Get emailVerifiedAt.
*
* @return \DateTime|null
*/
public function getEmailVerifiedAt()
{
return $this->emailVerifiedAt;
}
/**
* Set password.
*
* @param string|null $password
*
* @return Users
*/
public function setPassword($password = null)
{
$this->password = $password;
return $this;
}
/**
* Get password.
*
* @return string|null
*/
public function getPassword()
{
return $this->password;
}
/**
* Set rememberToken.
*
* @param string|null $rememberToken
*
* @return Users
*/
public function setRememberToken($rememberToken = null)
{
$this->rememberToken = $rememberToken;
return $this;
}
/**
* Get rememberToken.
*
* @return string|null
*/
public function getRememberToken()
{
return $this->rememberToken;
}
/**
* Set createdAt.
*
* @param \DateTime|null $createdAt
*
* @return Users
*/
public function setCreatedAt($createdAt = null)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt.
*
* @return \DateTime|null
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt.
*
* @param \DateTime|null $updatedAt
*
* @return Users
*/
public function setUpdatedAt($updatedAt = null)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt.
*
* @return \DateTime|null
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}
自動生成した際には、上書きしてしまうため、
自動生成後に別のディレクトリに移して
スキーマの変更があった時にはそのスキーマのクラスの変更部分のみ更新するのが良いかもしれません。
以上です。