Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

laravel-doctrine でDBからgetter, setter付きクラスの自動生成する

More than 1 year has passed since last update.

経緯

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を置き換えると自動生成が動くようでした。
(コードを生成後は元のコードに戻しておく、との記述がありました)

参照:https://medium.com/@joaoneto/solves-doctrine-orm-error-with-tables-without-primary-key-on-mysql-when-mapping-the-database-1ce740610b51

 //~~~~
           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;
    }
}

自動生成した際には、上書きしてしまうため、
自動生成後に別のディレクトリに移して
スキーマの変更があった時にはそのスキーマのクラスの変更部分のみ更新するのが良いかもしれません。

以上です。

ttn_tt
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away