Ground Sunlight

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

ユーザ用ツール

サイト用ツール


apricot:usage:ja:model

差分

このページの2つのバージョン間の差分を表示します。

この比較画面にリンクする

次のリビジョン
前のリビジョン
次のリビジョン 両方とも次のリビジョン
apricot:usage:ja:model [2020/07/29 13:27]
tanaka 作成
apricot:usage:ja:model [2020/08/05 09:45]
tanaka [サービスコンテナの使用例]
行 6: 行 6:
  --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-07-29//  --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-07-29//
  
-[[apricot:usage:ja|Apricotの使用法 に戻る]]+[[apricot:usage:ja|Apricot Document に戻る]]
  
 目次 目次
  
 +  * [[apricot:usage:ja:features|Apricot 特徴と概要]]
   * [[apricot:usage:ja:config|Apricot 配置と構成]]   * [[apricot:usage:ja:config|Apricot 配置と構成]]
 +  * [[apricot:usage:ja:errors-logging|Apricot ログとエラー処理]]
   * [[apricot:usage:ja:http|Apricot リクエストとレスポンス]]   * [[apricot:usage:ja:http|Apricot リクエストとレスポンス]]
   * [[apricot:usage:ja:frontend|Apricot フロントエンド]]   * [[apricot:usage:ja:frontend|Apricot フロントエンド]]
行 16: 行 18:
   * [[apricot:usage:ja:middleware|Apricot ミドルウェア]]   * [[apricot:usage:ja:middleware|Apricot ミドルウェア]]
   * [[apricot:usage:ja:controller|Apricot コントローラ]]   * [[apricot:usage:ja:controller|Apricot コントローラ]]
-  * [[apricot:usage:ja:errors-logging|Apricot ログとエラー処理]] 
   * [[apricot:usage:ja:utility|Apricot ユーティリティ]]   * [[apricot:usage:ja:utility|Apricot ユーティリティ]]
  
 ---- ----
  
-===== データベースの利用 ===== +===== ORMの設定 ===== 
->TODO+ 
 +ORマッパーには[[basic-library:idiorm:1.5|Idiorm]]を使用します。Idiormは元々シングルトンとして実装してあるのでそのまま使えます。使い方やメソッドについてはIdiormの[[https://idiorm.readthedocs.io/en/latest/|マニュアル]]を参照して下さい。 
 + 
 +==== 設定ファイル ==== 
 + 
 +{{fa>folder-open-o}} ** /apricot/config/setting ** 
 +<code php idiorm.setting.php> 
 +<?php 
 +/** 
 + * This file contains Idiorm settings. 
 + */ 
 +return 
 +
 +    'sqlite' => [ 
 +        'db_file' => var_dir('db/apricot.sqlite'), 
 +        'connection_string' => 'sqlite:'.var_dir('db/apricot.sqlite'), 
 +        'caching' => true, 
 +        'logging' => true, 
 +    ], 
 +    'initial_data' => [ 
 +        'user'=>
 +            'exec' =>[ 
 +                'delete from sqlite_sequence where name=\'user\'', 
 +            ], 
 +            'rows' => [ 
 +                [ 
 +                    'account' =>'root', 
 +                    'password' =>password_hash('', PASSWORD_DEFAULT), 
 +                    'email' =>'root@sample.com', 
 +                    'note' =>'Initial User', 
 +                ], 
 +            ], 
 +        ], 
 +    ], 
 +]; 
 +</code> 
 + 
 +  * sqlite : 接続設定(apricotではSQLiteを使用します) 
 +    * sqlite.db_file --- データベースファイルパス (既定値は var/db/apricot.sqlite'
 +    * sqlite.connection_string --- 接続文字列 
 +    * sqlite.caching --- キャッシングの有無 
 +    * sqlite.logging --- ロギングの有無 
 + 
 +  * initial_data : 初期データ (テーブルが空の時に自動設定されます) 
 +    * {テーブル名} --- ここではユーザテーブル( user )を指定しています 
 +      * exec --- 実行したいSQLを記述します(複数指定可) 
 +      * rows --- 行データを設定します(複数指定可) 
 + 
 +接続設定の詳細は以下を参照して下さい:\\  
 +https://idiorm.readthedocs.io/en/latest/configuration.html#id1 
 + 
 +\\ 
 + 
 +==== 初期設定ファイル ==== 
 + 
 +データベースの設定は初期設定ファイルで行います。 
 + 
 +{{fa>folder-open-o}} ** /apricot/config/setup ** 
 +<code php idiorm.setup.php> 
 +<?php 
 +/** 
 + * Initial setting of ORM (idirom) 
 + */ 
 +return function():bool 
 +
 +    // Prepares the database files 
 +    $db_file = config('idiorm.sqlite.db_file'); 
 +    if (!file_exists($db_path=dirname($db_file))) 
 +    { 
 +        @mkdir($db_path, null, true); 
 +    } 
 + 
 +    // Checks if DB file exists 
 +    $new_db_file = !file_exists($db_file); 
 + 
 +    // Connects to database 
 +    ORM::configure([ 
 +        'connection_string' => config('idiorm.sqlite.connection_string'), 
 +        'caching' => config('idiorm.sqlite.caching',false), 
 +        'logging' => false, 
 +        'logger' => function($log_string, $query_time) 
 +        { 
 +            // SQL debug logging 
 +            Apricot\Log::info("SQL",[$log_string]); 
 +        }, 
 +    ]); 
 + 
 +    //------------------------------------------- 
 +    // Creates tables when creating a new DB 
 +    //------------------------------------------- 
 +    if ($new_db_file) 
 +    { 
 +        $sql_text = file_get_sql(assets_dir('sql/create.sql')); 
 +        if (!empty($sql_text)) 
 +        { 
 +            foreach($sql_text as $sql) 
 +            { 
 +                ORM::get_db()->exec($sql); 
 +            } 
 +        } 
 +    } 
 + 
 +    //------------------------------------------- 
 +    // Creates initial data when a table is empty 
 +    //------------------------------------------- 
 +    $initial_data = config('idiorm.initial_data'); 
 +    if (isset($initial_data)) 
 +    { 
 +        foreach($initial_data as $key=>$item) 
 +        { 
 +            if(ORM::for_table($key)->find_one()===false) 
 +            { 
 +                // SQL execution 
 +                if (array_key_exists('exec', $item)) 
 +                { 
 +                    $exec = (array)$item['exec']; 
 +                    foreach($exec as $sql) 
 +                    { 
 +                        ORM::get_db()->exec($sql); 
 +                    } 
 +                } 
 + 
 +                // Creates new records 
 +                if (array_key_exists('rows', $item)) 
 +                { 
 +                    $rows = (array)$item['rows']; 
 +                    foreach($rows as $row) 
 +                    { 
 +                        $row = ORM::for_table($key)->create($row); 
 +                        $row->set_expr('created_at', "datetime('now')"); 
 +                        $row->set_expr('updated_at', "datetime('now')"); 
 +                        $row->save(); 
 +                    } 
 +                } 
 +            } 
 +        } 
 +    } 
 + 
 +    // Starts SQL log 
 +    ORM::configure('logging' , config('idiorm.sqlite.logging',false)); 
 +    return true; // Must return true on success 
 +}; 
 +</code> 
 + 
 +初期設定ファイルでは以下の事をおこないます: 
 + 
 +  * データベースの保存フォルダが存在しない場合は作成します 
 +  * データベースへの接続 
 +  * テーブルの作成 (新しくDBを作った時) 
 +  * 初期ユーザの作成 (ユーザテーブルが空の時) 
 +  * SQLログの開始 (logging設定がtrueの場合) 
 + 
 +\\ 
 + 
 + 
 +===== データベースの構築 ===== 
 + 
 +Apricotでは以下のユーザテーブルを持っています。 
 + 
 +テーブル名 : **user** 
 +^カラム名^型^主Key^属性^説明^ 
 +|id|integer|●|autoincrement|ID| 
 +|account|text| |unique not null|アカウント| 
 +|password|text| |not null|パスワード| 
 +|email|text| |not null|Eメールアドレス| 
 +|note|text| | |備考| 
 +|remember_token|text| | |自動ログイン用| 
 +|created_at|text| |not null|作成日| 
 +|updated_at|text| |not null|更新日| 
 +|version_no|integer| |default 0 not null|バージョンNo| 
 + 
 +  * ユーザ認証は(account,password)で行います 
 +  * remember_tokenは自動ログイン用の認証トークンです 
 +  * version_noは楽観的ロックで使用します
  
 \\ \\
  
 ===== モデル ===== ===== モデル =====
->TODO+ 
 +ORマッパーが使えるようになったので、モデルのベースクラス( Model )を作ります。Modelクラスは必ず継承して使い、以下のメソッドを持ちます。詳しくはソースコードを参照して下さい。 
 + 
 +^メソッド名^機能^ 
 +|tableName()\\ :string|テーブル名の取得\\ テーブル名(snake_case)はクラス名(UpperCamelCase)から自動判定します。| 
 +|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|レコードの更新\\ レコードが存在しない時、ApplicationExceptionが発生します。\\ 楽観的ロック例外を検知した時、OptimissticLockExceptionが発生します。| 
 +|delete\\ ($id):ORM|レコードの削除\\ レコードが存在しない時、ApplicationExceptionが発生します。| 
 +|isSuccess()\\ :bool|最新の更新結果を取得します(insert/update/delete)|
  
 \\ \\
  
-===== サービスプロバイー ===== +===== サービスプロバイー ===== 
->TODO+ 
 +サービスコンテナを使用することで、サービスとサービス間の依存関係を登録しておいて後で取得することができます。例えば、サービスAがモデルBとモデルCを使用しているような場合、サービスコンテナにサービスAを要求すると、自動的にモデルBとCを生成し、それらをサービスAのコンストラクタに与えてサービスAを生成してくれます。 
 + 
 +サービスプロバイターは、アプリケーション内の全てのサービスコンテナを登録し整理する方法を提供してくれます。また、サービスプロバイダーではサービスが取得された時点で遅延登録されるため、アプリケーションのパフォーマンス向上にも寄与します。 
 + 
 +League/Container でサービスプロバイダーを構築するには以下のステップに従います。 
 + 
 +  - League/Container の 基本サービスプロバイダークラス( ''AbstractServiceProvider'' )を拡張して 独自のサービスプロバイダーを作ります。 
 +  - League/Container の ''Container'' クラスに 独自のサービスプロバイダーを登録します。 
 + 
 +Apricotでは、独自のサービスプロバイダーとして ''App\Provider'' クラスを定義し、それを登録する ''App\Foundation\Container'' クラスをシングルトンとして実装します。サービスの使用者は、''App\Foundation\Container''が持っている [[https://www.php-fig.org/psr/psr-11/|PSR-11]] に準じた ''get()'' と ''has()'' を使ってサービスを利用することができます。
  
 \\ \\
  
 +==== Providerクラス ====
 +
 +以下に、League/Container の 基本サービスプロバイダークラス( AbstractServiceProvider )を拡張したApricot独自のサービスプロバイダークラス( Provider )を以下に示します。
 +
 +{{fa>folder-open-o}} ** /apricot/app **
 +<code php 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 );
 +     }
 +}
 +</code>
 +
 +このクラスは、名前空間Appの直下に存在し、アプリケーションのモデル及びサービスのマップを提供します。現版のApricotでは、モデルはユーザモデル( User )だけで、サービスについては存在しません。モデルやサービスを追加する場合は、上例に習って適宜追加して下さい。
 +
 +現版のApricotでは、サービスは存在しませんが、サービス用として以下のフォルダが予約されています。
 +
 +<code>
 +/apricot/app/Services
 +</code>
 +
 +尚、League/Container のサービスプロバイダーについての詳細は[[https://container.thephpleague.com/3.x/service-providers/|こちら]]をご覧ください。
 +
 +\\
 +
 +==== App\Foundation\Containerクラス ====
 +
 +App\Foundation\Containerクラスは、\League\Container\Container クラスを生成し、Apricotのサービスプロバイダー(Provider)を登録したクラスで、シングルトンとして動作します。
 +
 +使用法: ** Container::{メソッド} **
 +
 +^メソッド^機能^
 +|mixed get(string $id)|識別子idでコンテナのエントリを検索して返します。|
 +|bool has(string $id)|コンテナが指定された識別子idのエントリを返すことができる場合はtrueを返します。|
 +
 +{{fa>folder-open-o}} ** /apricot/app/Foundation **
 +<code php 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;
 +    }
 +}
 +</code>
 +
 +\\
 +
 +==== サービスコンテナの使用例 ====
 +
 +=== スタブコントローラ ===
 +
 +サービスコンテナをテストするために、スタブコントローラを以下のように修正します。
 +
 +{{fa>folder-open-o}} ** /apricot/app/Controllers **
 +<code php 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]);
 +    }
 +}
 +</code>
 +
 +  * ''Container::get('user')'' でユーザモデルを生成します。
 +  * ユーザモデルの ''findAll()'' を実行して全ユーザのリストを取得します。
 +  * ユーザ数を表示するために、テンプレート変数 ''$messages'' をセットします。
 +
 +\\
apricot/usage/ja/model.txt · 最終更新: 2020/09/03 13:45 by y2sunlight