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

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@mogamoga1337

Zend Framework3 超入門

前書き

これはZendFramework3を5%理解した人が、
全く触ったことがない人にZendFramework3を3%くらい理解させる目的で書いた記事です。

バージョン

Zend Framework 3.1.3

プロジェクト作成&起動

プロジェクト作成

$ cd C:\xampp\htdocs\zendRensyu (お好きなところにプロジェクトフォルダを作って移動する。)
$ composer create-project zendframework/skeleton-application .

「Do you want a minimal install (no optional packages)? Y/n」と、聞かれるのでnを押す。
後は不要そうなら拒否しておく。よくわからないなら許可すればおk

起動

$ cd C:\xampp\htdocs\zendRensyu
$ php -S 0.0.0.0:80 -t public

http://localhostにアクセスするとWelcomeページが出る。
http://localhost/application/indexでも同じ。

apacheのhtdocsフォルダの配下にプロジェクトフォルダを作成した場合は
http://localhost/(プロジェクト名)/publicでアクセスできる。

Controller

ルーティング設定

編集するファイル
(プロジェクトフォルダ)\module\Application\config\module.config.php

下記の設定は
http://(ドメイン名)/(コンテキストパス)/hogeと、
HogeControllerを紐づけている。

return [
    'router' => [
        'routes' => [
            ・・・,
            'hoge' => [
                'type'    => Segment::class,
                'options' => [
                    'route'    => '/hoge[/:action]',
                    'defaults' => [
                        'controller' => Controller\HogeController::class,
                        'action'     => 'index',
                    ],
                ],
            ],
        ],
    ],
    'controllers' => [
        'factories' => [
            Controller\IndexController::class => InvokableFactory::class,
            Controller\HogeController::class => InvokableFactory::class, // ← 付け足す。
        ],
    ],
    ・・・

基本

(プロジェクトフォルダ)\module\Application\src\Controller\HogeController.php

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        $vm = new ViewModel();
        return $vm;
    }
}

(プロジェクトフォルダ)\module\Application\view\application\hoge\index.phtml

<div>
    Hogehoge
</div>

HogeController#indexActionにリクエストが来た際に、自動的にhoge\index.phtmlを元にhtmlが生成されレスポンスされる。
HogeController#hogeActionならhoge\hoge.phtmlが紐づけられる。

レンダリングに使うviewを明示的に指定する

(プロジェクトフォルダ)\module\Application\view\application\moga\moga.phtmlを指定する場合

public function indexAction()
{
    $vm = new ViewModel();
    $vm->setTemplate('application\moga\moga');
    return $vm;
}

Get送信からパラメータを受け取る

public function indexAction()
{
    // パラメータを連想配列で受け取る
    $params = $this->getRequest()->getQuery();

    return new ViewModel();
}

Post送信からパラメータを受け取る

public function indexAction()
{
    // パラメータを連想配列で受け取る
    $params = $this->getRequest()->getPost();

    return new ViewModel();
}

Viewに値を渡す

Controller

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        // 方法1
        $vm = new ViewModel(['param1' => 'ほげほげ']);

        // 方法2
        $vm->setVariable('param2', 'ふがふが');

        // 方法3
        $vm->setVariables([
            'param3' => 'ぴよぴよ',
            'param4' => 'もがもが'
        ]);

        // もちろんオブジェクトも渡せる
        $vm->setVariable('param5', new Nagonago('なごなご'));

        return $vm;
    }
}

class Nagonago {
    public $nago = null;
    function __construct($nago) { $this->nago = $nago; }
}

View

<div>
    Hogehoge<br>
    <hr>

    <?= $this->param1; ?><br>
    <?= $this->param2; ?><br>
    <?= $this->param3; ?><br>
    <?= $this->param4; ?><br>
    <?= $this->param5->nago; ?><br>
</div>

レスポンス
zend.png

レスポンスでJSONを返す

(プロジェクトフォルダ)\module\Application\config\module.config.php

return [
    ・・・,
    'view_manager' => [
        ・・・,
        // ↓ 追加
        'strategies' => [
            'ViewJsonStrategy',
        ],
    ],
];

Controller

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\View\Model\JsonModel;

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        return new JsonModel([
            'name' => 'mogamoga',
            'age' => 24,
            'messages' => [
                'hello',
                'good, morning'
            ],
            'data' => [
                'key1' => 'value1',
                'key2' => 'value2'
            ]
        ]);
    }
}

フォワード

基本

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        // FugaControllerのindexActionにフォワードさせる。
        return $this->forward()->dispatch(FugaController::class, ['action' => 'index']);
    }
}

パラメータを渡す

投げる側

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->forward()->dispatch(
            FugaController::class,
            [
                'action' => 'index',
                'human' => [
                    'name' => 'mogamoga',
                    'age' => 24
                ]
            ]
        );
    }
}

受け取る側

class FugaController extends AbstractActionController
{
    // こうやって受け取る
    $human = $this->params('human');

    return new ViewModel();
}

リダイレクト

基本

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        // toRouteメソッドの第一引数には、
        // (プロジェクトフォルダ)\module\Application\config\module.config.phpの
        // 'routes'の値に設定している連想配列のキーを指定する。
        return $this->redirect()->toRoute('fuga', ['action' => 'index']);
    }
}

パラメータを渡す

投げる側

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        // Get送信扱いでリダイレクトさせる。Post送信扱いする方法は知らない。
        return $this->redirect()->toRoute(
            'fuga',
            ['action' => 'index'],
            [
                'query' => [
                    'name' => 'mogamoga',
                    'age' => 24
                ]
            ]
        );
    }
}

受け取る側

class FugaController extends AbstractActionController
{
    public function indexAction()
    {
        // パラメータを連想配列で受け取る
        $params = $this->getRequest()->getQuery();

        return new ViewModel();
    }
}

外部にリダイレクトさせる

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->redirect()->toUrl('https://qiita.com/');
    }
}

View

(プロジェクトフォルダ)\module\Application\src\Controller\HogeController.php

class HogeController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel([
            'hoge' => 'ほげほげ',
            'fuga' => '<script>alert("Hello, ZF3!")</script>',
            'flag' => 1,
            'mogas' => [
                'このフレームワークの',
                '勉強しているの',
                '2020年時点で',
                '俺だけ説'
            ]
        ]);
    }
}

(プロジェクトフォルダ)\module\Application\view\application\hoge\index.phtml

<div>
    <h3>変数の表示</h3>
    <?= $this->hoge; ?>
    <br><hr>

    <h3>URL</h3>
    <!--
    urlメソッドの第一引数には、
    (プロジェクトフォルダ)\module\Application\config\module.config.phpの
    'routes'の値に設定している連想配列のキーを指定する。
    -->
    <?= $this->url('hoge', ['action' => 'hello']) ?>
    <br><hr>

    <h3>特殊文字のエスケープ</h3>
    <?= $this->escapeHtml($this->fuga); ?>
    <br><hr>

    <h3>if文</h3>
    <?php if ($this->flag === 0): ?>
        なごなご
    <?php elseif ($this->flag === 1): ?>
        ぴよぴよ
    <?php else: ?>
        もがもが
    <?php endif; ?>
    <br><hr>

    <h3>for文</h3>
    <?php foreach ($this->mogas as $moga): ?>
        <?= $moga ?><br>
    <?php endforeach; ?>

レスポンス
zend2.png

DBアクセス

前書き

ぶっちゃけ、分からん。
いろいろ方法があったり、設定が面倒だったり、おまじないが多かったりして
頭わるわるな自分には分からない。

とりあえず以下のサイトが参考になると思います。

Zend Framework3のチュートリアル日本語化メモ-4
Zend Framework3の始め方4
zendFramework3でdbアダプタをグローバルで持ちたい
ZF2を使っていろいろなSELECT文を生成する
Adapters - zend-db - Zend Framework Docs
SQL Abstraction - zend-db - Zend Framework Docs

オレオレDBアクセスラッパークラス

多分これが一番楽だと思います。

以下をコピペして利用してください。

<?php

namespace Application\Common;

use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
use Zend\Db\ResultSet\ResultSet;

class DBManager
{
    private $adapter = null;
    private $connection = null;
    private $sql = null;

    function __construct() {
        // 自分の環境にあわせて変えてください
        $config = [
            'driver' => 'mysqli',
            'host' => 'localhost',
            'username' => '(ユーザー名)',
            'password' => '(パスワード)',
            'database' => '(データベース名)',
            'charset' => 'utf8',
        ];
        $this->adapter = new Adapter($config);
        $this->connection = $this->adapter->getDriver()->getConnection();
        $this->sql = new Sql($this->adapter);
    }

    public function beginTransaction()
    {
        $this->connection->beginTransaction();
    }

    public function commit()
    {
        $this->connection->commit();
    }

    public function rollback()
    {
        $this->connection->rollback();
    }

    public function query(
        $sql,
        $parametersOrQueryMode = Adapter::QUERY_MODE_EXECUTE
    ) {
        $result = $this->adapter->query($sql, $parametersOrQueryMode);
        return ($result instanceof ResultSet) ? $result->toArray() : $result;
    }

    public function select(callable $selectFunction)
    {
        $select = $this->sql->select();
        $selectFunction($select);
        $resultsItr = $this->sql->prepareStatementForSqlObject($select)->execute();

        $results = [];
        foreach ($resultsItr as $result) $results[] = $result;

        return $results;
    }

    public function insert(callable $insertFunction)
    {
        $insert = $this->sql->insert();
        $insertFunction($insert);
        $this->sql->prepareStatementForSqlObject($insert)->execute();
    }

    public function update(callable $updateFunction)
    {
        $update = $this->sql->update();
        $updateFunction($update);
        $this->sql->prepareStatementForSqlObject($update)->execute();
    }

    public function delete(callable $deleteFunction)
    {
        $delete = $this->sql->delete();
        $deleteFunction($delete);
        $this->sql->prepareStatementForSqlObject($delete)->execute();
    }
}

トランザクション

以降もそうですが、コントローラ層で使用方法を説明しているのに特に意味はないです。
実際にはデータアクセス層にでも書いて利用してください。

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Common\DBManager;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        $db = new DBManager();

        // トランザクション開始
        $db->beginTransaction();

        // 略(いろんな処理とかSQLとか)

        // コミット
        $db->commit();
        // ロールバック
        //$db->rollback();

        return new ViewModel();
    }
}

生SQL

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Common\DBManager;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        $db = new DBManager();

        $db->beginTransaction();

        // delete
        $db->query("delete from characters where name = ?", ['ひで']);

        // insert
        $db->query('insert into characters(name, kana) values (?, ?)', ['野獣先輩', 'やじゅやじゅビースト']);
        $db->query('insert into characters(name, kana) values (?, ?)', ['MUR', 'みうら']);

        // update
        $db->query('update characters set kana = ? where name = ?', ['やじゅうせんぱい', '野獣先輩']);

        // select
        $characters = $db->query('select * from characters');
        // もちろん第二引数があってもいいですよ
        //characters = $db->query('select name, kana from characters where kana like ?', ['%や%']);

        echo '<pre>';
        echo var_dump($characters);
        echo '</pre>';

        $db->commit();

        return new ViewModel();
    }
}

select結果
zend3.png

クエリビルダー

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Common\DBManager;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        $db = new DBManager();

        $db->beginTransaction();

        // delete
        $db->delete(function($delete) {
            $delete->from('characters')
                ->where
                ->like('kana', '%や%');
        });

        // insert
        $db->insert(function($insert) {
            $insert->into('characters')
                ->columns(['name', 'kana'])
                ->values(['KMR', 'きむら']);
        });
        // 一回のinsertで複数行の挿入は無理っぽい
        // 参考:https://stackoverflow.com/questions/816910/how-do-i-add-more-than-one-row-with-zend-db
        $db->insert(function($insert) {
            $insert->into('characters')
                ->columns(['name', 'kana'])
                ->values(['TON', 'まずうち']);
        });

        // update
        $db->update(function($update) {
            $update->table('characters')
                ->set(['kana' => 'とおの'])
                ->where
                ->equalTo('name', 'TON');
        });

        // select
        $characters = $db->select(function($select) {
            $select->from('characters')
                ->where
                ->in('name', ['MUR', 'KMR', 'TON']);
        });

        echo '<pre>';
        echo var_dump($characters);
        echo '</pre>';

        $db->commit();

        return new ViewModel();
    }
}

select結果
zend4.png

チラ裏

情報少なすぎて泣ける。

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
4
Help us understand the problem. What are the problem?