publicディレクトリ内に直接ルーティングさせる
クライアント様のサーバに直接アプリケーションを設置する場合も多々あるため、ApacheWebサーバをそのまま用いてアクセスするようにする。
そのためmod_rewriteを用いて直接public内にルーティングするよう、下記 .htaccess
を設置する。
# ci4/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
<FilesMatch "^\.">
Require all denied
Satisfy All
</FilesMatch>
設定
参考:
index.php を削除する
# app/Config/App.php
/**
* --------------------------------------------------------------------------
* Index File
* --------------------------------------------------------------------------
*
* Typically, this will be your `index.php` file, unless you've renamed it to
* something else. If you have configured your web server to remove this file
* from your site URIs, set this variable to an empty string.
*/
// public string $indexPage = 'index.php';
public string $indexPage = '';
csrf フィルターを有効に
--- a/app/Config/Filters.php
+++ b/app/Config/Filters.php
@@ -34,7 +34,7 @@
public $globals = [
'before' => [
// 'honeypot',
- // 'csrf',
+ 'csrf',
// 'invalidchars',
],
'after' => [
より安全な Session ベースの保護に切り替え
--- a/app/Config/Security.php
+++ b/app/Config/Security.php
@@ -15,7 +15,7 @@
*
* @var string 'cookie' or 'session'
*/
- public $csrfProtection = 'cookie';
+ public $csrfProtection = 'session';
/**
* --------------------------------------------------------------------------
@@ -26,7 +26,7 @@
*
* @var bool
*/
- public $tokenRandomize = false;
+ public $tokenRandomize = true;
/**
* --------------------------------------------------------------------------
* CSRF Token Name
* --------------------------------------------------------------------------
*
* Token name for Cross Site Request Forgery protection.
*/
- public string $tokenName = 'csrf_test_name';
+ public string $tokenName = 'csrf_token';
session の設定
# app/Config/Session.php
// Session cookie name
public string $cookieName = 'ci4app_session';
// Session Expiration
public int $expiration = 3600;
.env と configの関係
.env での定義はconfigでの定義より優先される。
dotenv()
等を使う必要はない。
.env
# <ファイル名>.<プロパティ名>
hoge.name = 'hogehoge'
app/Config/Hoge.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Hoge extends BaseConfig
{
public string $name = 'fugafuga';
}
baseUrl, siteUrl
# app/Config/App.php
/**
* --------------------------------------------------------------------------
* Base Site URL
* --------------------------------------------------------------------------
*
* URL to your CodeIgniter root. Typically, this will be your base URL,
* WITH a trailing slash:
*
* E.g., http://example.com/form
*/
public string $baseURL = 'http://localhost:8080/my-app';
/**
* --------------------------------------------------------------------------
* Root Site URL
* --------------------------------------------------------------------------
*
* URL to your Site root. Typically, this will be your base URL,
* WITH a trailing slash:
*
* E.g., http://example.com/
*/
public string $siteURL = 'http://localhost:8080/';
# .env
app.baseURL = ''
app.siteURL = ''
locale, tz
diff --git a/app/Config/App.php b/app/Config/App.php
index f330a85..845e609 100644
--- a/app/Config/App.php
+++ b/app/Config/App.php
@@ -95,7 +95,7 @@ class App extends BaseConfig
* strings (like currency markers, numbers, etc), that your program
* should run under for this request.
*/
- public string $defaultLocale = 'en';
+ public string $defaultLocale = 'ja';
/**
* --------------------------------------------------------------------------
@@ -135,7 +135,7 @@ class App extends BaseConfig
* @see https://www.php.net/manual/en/timezones.php for list of timezones
* supported by PHP.
*/
- public string $appTimezone = 'UTC';
+ public string $appTimezone = 'Asia/Tokyo';
/**
* --------------------------------------------------------------------------
参照の方法
BaseController
でプロパティに定義した上で使う。
# app/Controllers/BaseController.php
protected $appConfig;
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
$this->appConfig = new \Config\App();
}
# app/Controllers/Home.php
public function index(): string
{
$this->appConfig->siteURL;
config()
ヘルパーで直接使う。
# app/Controllers/Home.php
public function index(): string
{
config('App')->baseURL;
Session
基本はSessionクラスをBaseController
でインスタンス化して使い、必要に応じてsessionヘルパーを使う。
BaseController でSessionクラスをインスタンス化する方法
Sessionクラスのインスタンスが全てのコントローラーで継承され、一貫性を持ってセッションを管理できる。
全てのコントローラでセッションを一貫して使う場合に適している。
セッションの設定や初期化を一か所で管理できるため、コードの重複を避けることができる。
フォームを作る時はこちらが適してるのでは?
# app/Controllers/BaseController.php
protected $session;
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
{
parent::initController($request, $response, $logger);
$this->session = \Config\Services::session();
}
sessionヘルパーを使う方法
必要な時にsession()
関数を呼び出すことができる。
コントローラーやメソッドのみの特定の場所でのみセッション機能を使用したい場合に適している。
条件によってセッションの挙動を変更したい時に便利。
helper('session');
session()->set('item', 'value');
$item = session()->get('item');
毎回のロードが手間だと感じたら、以下のように自動ロードができる。
ただし、Sessionクラスをインスタンス化する場合は自動ロードは冗長になるから注意。
# app/Controllers/BaseController.php
protected $helpers = ['session'];
flash
public function input(): string
{
// ...
if ($this->session->has('error')) {
$data['error'] = $this->session->get('error');
}
// ...
}
public function confirm(): RedirectResponse|string
{
if (!$this->session->has('posts')) {
return redirect(route: 'input')->with(key: 'error', message: '不正なアクセスです。フォームから入力してください。');
}
//
組み込みのEmailクラス
# app/Config/Email.php
# controller等
<?php
use Config\Services;
public function sendEmail()
{
$email = Services::email();
// $email = \Config\Services::email();
$email->setFrom('your@example.com', 'Your Name');
$email->setTo('someone@example.com');
$email->setCC('another@another-example.com');
$email->setBCC('them@their-example.com');
$email->setSubject('Email Test');
$email->setMessage('Testing the email class.');
if ($email->send()) {
echo "Email successfully sent";
} else {
$data = $email->printDebugger(['headers']);
print_r($data);
}
}
デプロイ
.env
の編集
# system-admin/.htaccess
Options +SymLinksIfOwnerMatch
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
<FilesMatch "^\.">
Require all denied
Satisfy All
</FilesMatch>
# my-app/public/.htaccess
# Options +SymLinksIfOwnerMatch コメントアウトする
pager
# controller
$pager = Services::pager();
$pager->setPath('result/index');
$page = (int)$pageNum;
$perPage = 20;
$facilities = array_slice($facilities, ($page - 1) * $perPage, $perPage);
$data['pagerLinks'] = $pager->makeLinks($page, $perPage, $count, 'my_pagination', $segment = 3);
# app/Config/Pager.php
public array $templates = [
'default_full' => 'CodeIgniter\Pager\Views\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head',
'my_pagination' => 'App\Views\pagination',
];
# view.twig
{{ pagerLinks|raw }}
# my_pagination.php
<?php $pager->setSurroundCount(1); ?>
<ul class="pager clearfix" id="search-pagenation">
<?php if ($pager->hasPrevious()) : ?>
<li class="prev">
<a href="<?= $pager->getPrevious() ?>" rel="prev"><</a>
</li>
<?php endif; ?>
<?php foreach ($pager->links() as $i => $link): ?>
<li <?= $link['active'] ? 'class="current"' : '' ?>>
<a href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNext()) : ?>
<li class="next">
<a href="<?= $pager->getNext() ?>" rel="next">></a>
</li>
<?php endif; ?>
</ul>
image
# controller
// 画象を指定された幅にリサイズします。指定の幅より元となる画像の横幅が大きい場合のみ、縮小されます。
try {
$info = \Config\Services::image('gd')
->withFile(path: self::FOR_WRITEABLE_DIR . 'tmp/'. $randomName)
->getFile()
->getProperties(true);
if ($info['width'] > 990) {
$image = \Config\Services::image(handler: 'gd');
$image->withFile(path: self::FOR_WRITEABLE_DIR . 'tmp/'. $randomName)
->resize(width: 990, height: 990, maintainRatio: true, masterDim: 'width')
->save(target: self::FOR_WRITEABLE_DIR . 'tmp/'. $randomName, quality: 90);
log_message('info', 'Image: Resized Done. Uploaded image width was more than 990px.');
}
else {
log_message('info', 'Image: Resized Unnecessary. Uploaded image width was less than 990px.');
}
}
catch (ImageException $e) {
log_message(level: 'error', message: $e->getMessage());
}
Form
ヘルパーに頼る方法と頼らない方法がある。
もともとは頼らない方法で実装していた。
が、頼る方がコード量も少なく済む。よきにsessionの管理を調整してくれる。
ヘルパーはコーディングを助けてくれるためのツール。ただ、使い勝手をしらない間は使わない方が良い。
Form Helper
復元
set_value(string $field, ?string $default, ?bool $html_escape = true)
<input type="text" name="quantity" value="<?= set_value('quantity', '0') ?>" size="50">
set_select(string $field, ?string $value, ?string $default = false)
<select name="myselect">
<option value="one" <?= set_select('myselect', 'one', true) ?>>One</option>
<option value="two" <?= set_select('myselect', 'two') ?>>Two</option>
<option value="three" <?= set_select('myselect', 'three') ?>>Three</option>
</select>
set_checkbox(string $field, ?string $value, ?string $default = false)
<input type="checkbox" name="mycheck[]" value="1" <?= set_checkbox('mycheck', '1') ?>>
<input type="checkbox" name="mycheck[]" value="2" <?= set_checkbox('mycheck', '2') ?>>
set_radio(string $field, ?string $value, ?string $default = false)
<input type="radio" name="myradio" value="1" <?= set_radio('myradio', '1', true) ?>>
<input type="radio" name="myradio" value="2" <?= set_radio('myradio', '2') ?>>
Validation
Validation Interface
controllerにおいて、
$this->validator
も$validation = \Config\Services::validation()
も Validation\ValidationInterface
を実装している。故に同じ関数を使える。
ただ、run()
関数においては少し挙動が異なるから注意。
$this->validator->run($data, 'input')
はこの実行前にvalidationクラスをインスタンス化しないとエラーになる。
$validation->run($data, 'input')
は内部的にvalidationクラスをインスタンス化しているため使える。
$this->validateData()
関数等は内部的にValidationクラスをインスタンス化して、便利に使いやすいようにしているだけで、実質的にはValidationクラスを使っているだけ。
$this->validatedata(data: $data, rule: $rule)
は第二引数にデフォルトではルールの配列を期待しているが、ここにはルールグループ名を渡しても大丈夫。
Redirect and Validation Errors
PHPはリクエスト間でデータの共有をしない。例えば、前のリクエストに含まれていつpost値やクエリパラメータを次のリクエストに持ち越せない。つまり、バリデーション失敗時にリダイレクトすると思うが、そのバリデーションのリクエストと、リダイレクトするリクエストは別のリクエストであるため、バリデーション失敗のエラーメッセージ等を持ち越せないということになる。持ち越せないということは、Formヘルパーのvalidation_errors()
関数等が使えない。
これを解決するために、WithInput()
関数を使う。一時的にセッションにバリデーションエラーを追加してくれる。リロードで消えてしまうが。
// In Controller.
if (! $this->validateData($data, $rules)) {
return redirect()->back()->withInput();
}
Working With Errors
基本は、 system/Language/en/Validation.php を使って表示している。
オーバライドするには、 app/Language/en/Validation.php をいじる。
他言語の場合は、 app/Language/ja/Validation.php のように対象言語ディレクトリを作っていじる。
Creating Custom Rules
Using Rule Classes
参考:
https://codeigniter.com/user_guide/libraries/validation.html#creating-a-rule-class
# app/Config/Validation.php
class Validation extends BaseConfig
{
// ...
public array $ruleSets = [
Rules::class,
FormatRules::class,
FileRules::class,
CreditCardRules::class,
];
// ...
}
class MyRules
{
public function even($value, $params, $data): bool
{
return (int) $value % 2 === 0;
}
public function is_same($value, $params, $data): bool
{
// $post = $_POST;
// $otherValue = $post['otherValue']
$otherValue = $data[$param[0]];
return $value === $otherValue;
}
}
引数$value
に検証する値を。返り値はbool。$params
を使う時はルール呼び出し箇所に[fieldName]
を追加する。そうすると$data
に全てのpostされているデータが入ってくる。あとはkeyをfieldNameで指定してあげれば値が取れる。
$validation->setRules([
'foo' => 'required|max_length[19]|even|is_same[other]',
]);