Ground Sunlight

Windowsで作る - PHPプログラミングの開発環境

ユーザ用ツール

サイト用ツール


サイドバー

メインメニュー

XAMPP アレンジ

IED

WSL2

道具箱

リポジトリ編

フレームワーク編

公開ソフトウェア

メタ
リンク


このページへのアクセス
今日: 2 / 昨日: 0
総計: 980

apricot:usage:ja:model

文書の過去の版を表示しています。


編集中

Apricot モデルとサービス

y2sunlight 2020-07-29

Apricot ドキュメント に戻る

目次


モデルはデータベース上のテーブルと1対1に対応したクラスで、データベースからデータを取得または設定するためのメソッドを持っています。モデルのベースクラスはIdiormを使用して作成しています。

Apricotは比較的小規模なアプリケーション開発をターゲットにしているので、コントローラとモデルによってアプリケーションの作成が可能であると仮定しています。しかし、2つ以上のモデルを操作するような少し複雑なトランザクションが必要な場合は、それをサービスとして実装する必要があるかもしれません。

サービスに関する明確な指針を提供してはいませんが、Apricotのコアが使用している League/Container はサービスに関する強力なツールになるはずです。Apricotのスケルトンでは、このツールを使ってサービスプロバイターが利用できる仕組みと簡単な例題を提供しています。

サービスに関する説明はコントローラまたはサービスプロバイダーの章で説明します。


Modelクラス

モデルのベースクラスは、App\Foundation\Model です。全てのモデルクラスはこのクラスから継承して作成します。Modelから継承したクラスは以下の場所に配置することを推奨していますが、これは必須ではありません。適宜アプリケーションのルールで変更して下さい。

/your-project/app/Models

ModelクラスはほとんどのことをIdiormのORMクラスに頼っていますが、アプリケーションの共通的な処理を追加しています。Apricotのスケルトンでは、アプリケーションが使用する全てのテーブルで created_at、updated_at、version_no の3つカラムが存在します。

  • 新しくレコードが挿入される時、created_atupdated_at が 設定されます。
  • レコードが変更される時、created_atversion_no が 設定されます。
  • version_no はレコードのバージョンを表し、楽観的ロックで使用されます。

これらの共通処理はアプリケーションに依存するところが大きく、必要に応じて、Modelクラスを変更して下さい。


命名規則

クラス名とテーブル名の間の名前には以下の例のような命名規則があります。クラス名は Upper Camel (いわゆるPascalケース)、テーブル名はSnakeケースです。

クラス名テーブル名
Useruser
UserFrienduser_friend

クラスのフィールド名とテーブルのカラム名は同じです。両者に命名規則はありません。


Modelのメソッド

Modeクラスは以下のメソッドを持ちます。

メソッド機能
tableName():stringテーブル名の取得
for_table():ORMORMオブジェクトの取得
findAll():array全件検索(ORMの配列を返します)
findOne(int $id):mixed主キー検索(ORMまたはfalseを返します)
create(array $inputs=null):ORMモデルの新規作成
insert(array $inputs):ORMレコードの挿入
update($id, array $inputs):ORMレコードの更新
delete($id):ORMレコードの削除
isSuccessful():bool最新の更新結果の成否の取得

tableName()

tableName() は命名規則に従ってクラス名からテーブル名をを取得するメソッドです。

$user = new User();
$name = $user->tableName(); // return 'user'

forTable()

ORMのforTable()をラップしたメソッドで、ORMオブジェクトを取得します。

$orm = $user->forTable();

findAll()

モデルの対象となるテーブルから全件を検索するメソッドです。このメソッドはORMの配列を返します。

$users = $user->findAll();
foreach($users as $user)
{
    $account = $user->account;
}

findOne()

モデルの対象となるテーブルから主キーによる検索を行うメソッドです。見つかった場合は ORM を、それ以外は false を返します。

$user = $user->findOne($id);
if ($user !== false)
{
    // Failure
}
else
{
    $account = $user->account;
}

create()

新しいORMオブジェクトを返します。

$new_user = $user->create();

新しいORMオブジェクトにはモデルのフィールドがありません。モデルに初期値を設定するには以下のようにします。

$new_user = $user->create(['account'=>'new_account']);

insert()

指定されたモデルデータをテーブルに挿入します。このメソッドはORMオブジェクトを返します。

$inputs = Input::all();
 
$user = $user->insert($inputs);
$new_id = $user->id;

update()

指定されたモデルデータでテーブルを更新します。このメソッドはORMオブジェクトを返します。レコードが存在しない時、ApplicationException がスローされ、また、楽観的ロック例外を検知した時は、OptimissticLockException がスローされます。これらの例外については アプリの例外クラス も参照して下さい。

$inputs = Input::all();
 
try
{
    $user->update($inputs['id'], $inputs);
}
catch(ApplicationException $e)
{
    // OptimissticLockException is also an ApplicationException.
    // Do something.
}

delete()

指定された主キーのレコードをテーブルから削除します。このメソッドはORMオブジェクトを返します。レコードが存在しない時、ApplicationExceptionをスローします。

try
{
    $user->delete($id);
}
catch(ApplicationException $e)
{
    // Do something.
}

isSuccessful()

最新の更新結果をブーリアンで返します。対象は insert()、update()、delete()の各メソッドです。

$successful = $user->isSuccessful();


Modelの継承

以下はもっとも簡単なModelの継承の例です。Modelを継承するだけで、Modelクラスのメソッドが利用でき、簡単なモデルなら直ぐにアクションの実装に取り掛かれます。

User.php
<?php
namespace App\Models;
use App\Foundation\Model;
 
/**
 * User Model
 */
class User extends Model
{
}

次の例は、Apticotのスケルトンで提供されているユーザモデルです。この例では、insert()とupdate()をオーバーロードして個別の処理を追加しています。

User.php
<?php
namespace App\Models;
use App\Foundation\Model;
use ORM;
 
/**
 * User Model
 */
class User extends Model
{
    /**
     * {@inheritDoc}
     * @see \App\Foundation\Model::insert()
     */
    public function insert(array $inputs):ORM
    {
        // Encrypt the password that is required for new registration.
        $inputs['password'] = password_hash($inputs['password'], PASSWORD_DEFAULT);
 
        return parent::insert($inputs);
    }
 
    /**
     * {@inheritDoc}
     * @see \App\Foundation\Model::update()
     */
    public function update($id, array $inputs):ORM
    {
        // Updates a password only if entered
        if(empty($inputs['password'])) unset($inputs['password']);
 
        if(array_key_exists('password', $inputs))
        {
            // Encrypts the entered password
            $inputs['password'] = password_hash($inputs['password'], PASSWORD_DEFAULT);
        }
 
        return parent::update($id, $inputs);
    }
}

insert() では、パスワードを暗号化して保存しています。また、update() では、パスワードが入力された場合のみ暗号化し、そうでない場合は、入力変数からてパスワードを除外しています。このように、個別のモデルでは必要に応じてメソッドをオーバーライドしたり追加したりして下さい。


サービスプロバイター

DIコンテナ

ApricotではDIコンテナに League/Container を使用しています。

DIコンテナを使用することで、サービスとその依存関係を登録しておいて後で取得することができます。例えば、サービスAがモデルBとモデルCを使用しているような場合、サービスコンテナにサービスAを要求すると、自動的にモデルBとCを生成し、それらをサービスAのコンストラクタに与えてサービスAを生成してくれます。これがDIコンテナです。

ここでは、League/Container の慣例に従いDIコンテナと呼んでますが、サービスコンテナと呼んでも何の問題もありません。


Providerクラス

サービスプロバイターは、アプリケーション内の全てのDIコンテナを登録し整理する方法を提供してくれます。また、サービスプロバイダーではサービスが取得された時点で遅延登録されるため、アプリケーションのパフォーマンス向上にも寄与します。

このサービスプロバイターを作るには、League/Container が提供している基本サービスプロバイダークラス( AbstractServiceProvider )を拡張して独自のサービスプロバイダーを作ります。Apricotでは、独自のサービスプロバイダーとして App\Provider クラスを定義しています。以下にApricotのスケルトンで初期実装されているProviderクラスを示します。

/your-project/app

Provider.php
<?php
namespace App;
 
use League\Container\ServiceProvider\AbstractServiceProvider;
 
/**
 * Provider class for service
 */
class Provider extends AbstractServiceProvider
{
    /**
     * The provided array is a way to let the container
     * know that a service is provided by this service
     * provider. Every service that is registered via
     * this service provider must have an alias added
     * to this array or it will be ignored.
     *
     * @var array
     */
    protected $provides = [
        // Example
        'user',
    ];
 
    /**
     * This is where the magic happens, within the method you can
     * access the container and register or retrieve anything
     * that you need to, but remember, every alias registered
     * within this method must be declared in the `$provides` array.
     */
     public function register()
    {
        // Example
        $this->getContainer()->add('user', \App\Models\User::class );
     }
}

このクラスは、名前空間Appの直下に存在し、アプリケーションのモデル及びサービスのマップを提供します。現版のApricotでは、モデルはユーザモデル( User )だけで、サービスについては存在しません。モデルやサービスを追加する場合は、上例に習って適宜追加して下さい。

現版のApricotでは、サービスは存在しませんが、サービス用として以下のフォルダが予約されています。

/apricot/app/Services

尚、League/Container のサービスプロバイダーについての詳細はこちらをご覧ください。

、それを登録する App\Foundation\Container クラスをシングルトンとして実装します。サービスの使用者は、App\Foundation\Containerが持っている PSR-11 に準じた get()has() を使ってサービスを利用することができます。


Providerクラス

以下に、League/Container の 基本サービスプロバイダークラス( AbstractServiceProvider )を拡張したApricot独自のサービスプロバイダークラス( Provider )を以下に示します。


App\Foundation\Containerクラス

App\Foundation\Containerクラスは、\League\Container\Container クラスを生成し、Apricotのサービスプロバイダー(Provider)を登録したクラスで、シングルトンとして動作します。

使用法: Container::{メソッド}

メソッド機能
mixed get(string $id)識別子idでコンテナのエントリを検索して返します。
bool has(string $id)コンテナが指定された識別子idのエントリを返すことができる場合はtrueを返します。

/apricot/app/Foundation

Container.php
<?php
namespace App\Foundation;
 
use Apricot\Foundation\Singleton;
use App\Provider;
 
/**
 * Container class for service
 *
 * @method static Container getInstance() Gets the Container instance.
 * @method static mixed get(string $id) Finds an entry of the container by its identifier and returns it.
 * @method static bool has(string $id) Returns true if the container can return an entry for the given identifier.
 */
class Container extends Singleton
{
    /**
     * Create Container instance.
     * @return \League\Container\Container
     */
    protected static function createInstance()
    {
        $container = new \League\Container\Container;
        $container->addServiceProvider(new Provider());
        return $container;
    }
}


サービスコンテナの使用例

スタブコントローラ

サービスコンテナをテストするために、スタブコントローラを以下のように修正します。

/apricot/app/Controllers

StubController.php
<?php
namespace App\Controllers;
 
use App\Foundation\Container;
use App\Foundation\Controller;
 
/**
 * Stub Controller
 */
class StubController extends Controller
{
    /**
     * Index Page for this controller.
     *
     * @return \Apricot\Foundation\Response
     */
    public function index(int $no=null)
    {
        $title = "Stub {$no}";
 
        /*
         * Example for Container
         * @var \App\Models\User $user
         */
        $user = Container::get('user');
        $userCount = count($user->findAll());
        $messages[] = "Number of registered users : {$userCount}";
 
        return render('stub',['title'=>$title,'messages'=>$messages]);
    }
}
  • Container::get('user') でユーザモデルを生成します。
  • ユーザモデルの findAll() を実行して全ユーザのリストを取得します。
  • ユーザ数を表示するために、テンプレート変数 $messages をセットします。


コメント

コメントを入力. Wiki文法が有効です:
 
apricot/usage/ja/model.1597997694.txt.gz · 最終更新: 2020/08/21 17:14 by y2sunlight