メインメニュー
XAMPP アレンジ
IED
WSL2
道具箱
リポジトリ編
フレームワーク編
公開ソフトウェア
メタ
リンク
- PHP ライブラリ
- PHPDoc リファレンス
このページへのアクセス
今日: 2 / 昨日: 0
総計: 1250
このページへのアクセス
今日: 2 / 昨日: 0
総計: 1250
文書の過去の版を表示しています。
編集中
— y2sunlight 2020-07-29
目次
モデルはデータベース上のテーブルと1対1に対応したクラスで、データベースからデータを取得または設定するためのメソッドを持っています。モデルのベースクラスはIdiormを使用して作成しています。
Apricotは比較的小規模なアプリケーション開発をターゲットにしているので、コントローラとモデルによってアプリケーションの作成が可能であると仮定しています。しかし、2つ以上のモデルを操作するような少し複雑なトランザクションが必要な場合は、それをサービスとして実装する必要があるかもしれません。
Apricotでは、 アクションがモデル(またはサービス)に依存
モデルを
Actionはそのインタフェースに対してアクセスするように処理を実装します。 DIコンテナは、インタフェースに対する実装の選択機能を提供します。
Apricotでは、
サービスに関する明確な指針を提供してはいませんが、Apricotのコアが使用している League/Container はサービスに関する強力なツールになるはずです。Apricotのスケルトンでは、このツールを使ってサービスプロバイターが利用できる仕組みと簡単な例題を提供しています。
モデルのベースクラスは、App\Foundation\Model
です。全てのモデルクラスはこのクラスから継承して作成します。Modelから継承したクラスは以下の場所に配置することを推奨していますが、これは必須ではありません。適宜アプリケーションのルールで変更して下さい。
/your-project/app/Models
ModelクラスはほとんどのことをIdiormのORMクラスに頼っていますが、アプリケーションの共通的な処理を追加しています。Apricotのスケルトンでは、アプリケーションが使用する全てのテーブルで created_at、updated_at、version_no の3つカラムが存在します。
created_at
とupdated_at
が 設定されます。created_at
とversion_no
が 設定されます。version_no
はレコードのバージョンを表し、楽観的ロックで使用されます。これらの共通処理はアプリケーションに依存するところが大きく、必要に応じて、Modelクラスを変更して下さい。
クラス名とテーブル名の間の名前には以下の例のような命名規則があります。クラス名は Upper Camel (いわゆるPascalケース)、テーブル名はSnakeケースです。
クラス名 | テーブル名 |
---|---|
User | user |
UserFriend | user_friend |
クラスのフィールド名とテーブルのカラム名は同じです。両者に命名規則はありません。
Modeクラスは以下のメソッドを持ちます。
メソッド | 機能 |
---|---|
tableName():string | テーブル名の取得 |
for_table():ORM | ORMオブジェクトの取得 |
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() は命名規則に従ってクラス名からテーブル名をを取得するメソッドです。
$user = new User(); $name = $user->tableName(); // return 'user'
ORMのforTable()をラップしたメソッドで、ORMオブジェクトを取得します。
$orm = $user->forTable();
モデルの対象となるテーブルから全件を検索するメソッドです。このメソッドはORMの配列を返します。
$users = $user->findAll(); foreach($users as $user) { $account = $user->account; }
モデルの対象となるテーブルから主キーによる検索を行うメソッドです。見つかった場合は ORM を、それ以外は false を返します。
$user = $user->findOne($id); if ($user !== false) { // Failure } else { $account = $user->account; }
新しいORMオブジェクトを返します。
$new_user = $user->create();
新しいORMオブジェクトにはモデルのフィールドがありません。モデルに初期値を設定するには以下のようにします。
$new_user = $user->create(['account'=>'new_account']);
指定されたモデルデータをテーブルに挿入します。このメソッドはORMオブジェクトを返します。
$inputs = Input::all(); $user = $user->insert($inputs); $new_id = $user->id;
指定されたモデルデータでテーブルを更新します。このメソッドはORMオブジェクトを返します。レコードが存在しない時、ApplicationException
がスローされ、また、楽観的ロック例外を検知した時は、OptimissticLockException
がスローされます。これらの例外については アプリの例外クラス も参照して下さい。
$inputs = Input::all(); try { $user->update($inputs['id'], $inputs); } catch(ApplicationException $e) { // OptimissticLockException is also an ApplicationException. // Do something. }
指定された主キーのレコードをテーブルから削除します。このメソッドはORMオブジェクトを返します。レコードが存在しない時、ApplicationExceptionをスローします。
try { $user->delete($id); } catch(ApplicationException $e) { // Do something. }
最新の更新結果をブーリアンで返します。対象は insert()、update()、delete()の各メソッドです。
$successful = $user->isSuccessful();
以下はもっとも簡単なModelの継承の例です。Modelを継承するだけで、Modelクラスのメソッドが利用でき、簡単なモデルなら直ぐにアクションの実装に取り掛かれます。
<?php namespace App\Models; use App\Foundation\Model; /** * User Model */ class User extends Model { }
次の例は、Apticotのスケルトンで提供されているユーザモデルです。この例では、insert()とupdate()をオーバーロードして個別の処理を追加しています。
<?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()
では、パスワードが入力された場合のみ暗号化し、そうでない場合は、入力変数からてパスワードを除外しています。このように、個別のモデルでは必要に応じてメソッドをオーバーライドしたり追加したりして下さい。
ApricotではDIコンテナに League/Container を使用しています。
DIコンテナを使用することで、サービスとその依存関係を登録しておいて後で取得することができます。例えば、サービスAがモデルBとモデルCを使用しているような場合、サービスコンテナにサービスAを要求すると、自動的にモデルBとCを生成し、それらをサービスAのコンストラクタに与えてサービスAを生成してくれます。これがDIコンテナです。
ここでは、League/Container の慣例に従いDIコンテナと呼んでますが、サービスコンテナと呼んでも何の問題もありません。
サービスプロバイターは、アプリケーション内の全てのDIコンテナを登録し整理する方法を提供してくれます。また、サービスプロバイダーではサービスが取得された時点で遅延登録されるため、アプリケーションのパフォーマンス向上にも寄与します。
このサービスプロバイターを作るには、League/Container が提供している基本サービスプロバイダークラス( AbstractServiceProvider
)を拡張して独自のサービスプロバイダーを作ります。Apricotでは、独自のサービスプロバイダーとして App\Provider
クラスを定義しています。以下にApricotのスケルトンで初期実装されているProviderクラスを示します。
/your-project/app
<?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()
を使ってサービスを利用することができます。
以下に、League/Container の 基本サービスプロバイダークラス( AbstractServiceProvider )を拡張したApricot独自のサービスプロバイダークラス( Provider )を以下に示します。
App\Foundation\Containerクラスは、\League\Container\Container クラスを生成し、Apricotのサービスプロバイダー(Provider)を登録したクラスで、シングルトンとして動作します。
使用法: Container::{メソッド}
メソッド | 機能 |
---|---|
mixed get(string $id) | 識別子idでコンテナのエントリを検索して返します。 |
bool has(string $id) | コンテナが指定された識別子idのエントリを返すことができる場合はtrueを返します。 |
/apricot/app/Foundation
<?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
<?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
をセットします。
コメント