目的
CakePHP4の勉強がてら、Railsのプロジェクトを置き換える作業を実施した。
環境
Ruby On Rails 3.2.11
CakePHP 4.3.5
基本文法の比較
識別子
Ruby
local_var # ローカル変数
@instans_var # インスタンス変数
@@class_var # クラス変数
$global_var # グローバル変数
PHP
class Sample {
// インスタンス変数
static $instans_var;
// クラス変数
$class_var;
public $public_class_var;
private $private_class_var;
function classMethod() {
global $global_var;
echo $global_var
self::$instans_var = 'instans';
$this->class_var = 'class';
$local_var = 'local';
}
}
$global_var = 'global';
echo Sample::$instans_var;
$sample = new Sample;
echo $sample->class_var; // クラス変数
文字列操作
Ruby
# 結合
str = str1 + str2
str += str3
# 部分取得
str = str[0,3]
# 分割
str = str.split(',')
PHP
# 結合
$str = $str1 . $str2;
$str .= $str3;
# 部分取得
$str = substr($str,0,3);
# 分割
$str = explode(',', $str);
配列
Ruby
# Array
ary = [1]
ary << 2 # [1,2]
ary.push 3, 4 # [1,2,3,4]
puts ary[0] # 1
# Hash
h = {a:1, b:2}
h[:c] = 3 # {a:1, b:2, c:3}
PHP
# Array
$ary = [1];
$ary[] = 2; // [1,2]
array_push($ary, 3, 4); // [1,2,3,4]
puts $ary[0]l // 1
# 連想配列
$h = ['a'=>1, 'b'=>2];
$h['c'] = 3; // ['a'=>1, 'b'=>2, 'c'=>3]
if文
Ruby
if str.nil?
# 処理1
elsif str.blank?
# 処理2
else
# 処理3
end
PHP
if (is_null($str)) {
# 処理1
} else if ($str=="") {
# 処理2
} else {
# 処理3
}
for
Ruby
for i in 0..100 do
# 処理
end
for row in data do
# 処理
end
PHP
for ($i=0;$i<=100;$i++) {
# 処理
}
foreach ($data as $row) {
# 処理
}
each
Ruby
array.each do |val|
# 処理
end
hash.each do |key, val|
# 処理
end
hash.each_with_index do |val, i|
# 処理
end
PHP
foreach ($hash as $val) {
# 処理
}
foreach ($hash as $key => $val) {
# 処理
}
foreach ($hash as $i => $val) {
# 処理
}
関数定義
Ruby
def hello args
# 処理
ret # 戻り値
end
PHP
function hello($args) {
# 処理
return $ret; // 戻り値
}
クラス定義
Ruby
class ApplicationController < ActionController::Base
NUMBER_1 = '1'
def initialize(name)
@name = name
end
def classmethod
# 処理
end
end
PHP
class ApplicationController extends AppController
{
const NUMBER_1 = '1';
private $name;
public function __construct($name){
$this->name = $name;
}
public function classmethod(){
}
}
フレームワークの比較
css, js の記述
Ruby
<%= stylesheet_link_tag "base.css" %>
<%= stylesheet_link_tag "jquery.css" %>
<%= javascript_include_tag "base.js" %>
<%= javascript_include_tag "jquery.js" %>
PHP
<?= $this->Html->css(['base', 'jquery']) ?>
<?= $this->fetch('css') ?>
<?= $this->Html->script(['base', 'jquery']) ?>
<?= $this->fetch('script') ?>
コントローラ共通モジュール(ヘルパー)
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include CommonModule # /lib/common_module.rb
end
/src/Controller/ApplicationController.php
class ApplicationController < AppController
{
public function initialize(): void
{
$this->loadComponent('CommonModule');
$this->CommonModule->classMethod();
}
}
/src/Controller/Component/CommonModuleComponent.php
namespace App\Controller\Component;
use Cake\Controller\Component;
class CommonModuleComponent extends Component {
public function classMethod() {
}
}
ビューでコントローラの共通モジュール(Component)を使う
/src/View/Helper/CommonModuleHelper.php
namespace App\View\Helper;
use Cake\View\Helper;
use App\Controller\Component\CommonModuleComponent;
use Cake\Controller\ComponentRegistry;
class CommonModuleHelper extends Helper
{
function __call($methodName, $args) {
$common = new CommonModuleComponent(new ComponentRegistry());
return call_user_func_array(array($common, $methodName), $args);
}
}
共通処理(before_filter)
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter :init, only=>[:hoge1, :hoge2]
# 以下略
end
/src/Controller/ApplicationController.php
use Cake\Event\EventInterface;
class ApplicationController extends AppController {
public function beforeFilter(EventInterface $event)
{
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
$action = $this->getRequest()->getParam('action');
if (!in_array($action, ['hoge1', 'hoge2'])) return;
$this->init();
}
}
セッション
Ruby
# 代入・更新
session[:id] = 'hoge'
# 参照
@hoge = session[:id]
# 削除
session[:id].clear
session[:id] = nil
session.delete(:id)
# 全削除
reset_session
PHP
# セッションの取得
$session = $this->getRequest()->getSession();
# 代入・更新
$session->write('id', 'hoge');
# 参照
$hoge = $session->read('id');
# 存在し null ではない
$session->check('id');
# セッションの破棄
$session->destroy();
バイナリデータ出力(send_data)
Ruby
send_data(data, :type=>'application/octet-stream', :filename=>'ファイル名', :disposition=>'attachment')
PHP
public function send_data($data, $options=[]) {
$this->autoRender = false;
$filename = $options['filename'];
header("Content-type: ".$options['type']);
header('Content-Disposition: '.$options['disposition'].'; filename="'.$filename.'"');
echo stream_get_contents($data);
exit;
}
Model
Railsと同名関数を使える共通モデルを作成
PHP
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Table;
/**
* Common Model
*/
class CommonTable extends Table
{
public $connection;
public function initialize(array $config): void
{
parent::initialize($config);
$this->connection = \Cake\Datasource\ConnectionManager::get('default');
}
public function insert($params) {
return $this->query()->insert(array_keys($params))->values($params)->execute();
}
# .delete_all(:conditions=>[])
public function delete_all($conditions=[]) {
return $this->query()->delete()->where($conditions)->execute();
}
# .update_all(params, :conditions=>[])
public function update_all($params, $conditions=[]) {
return $this->query()->update()->set($params)->where($conditions)->execute();
}
# .find_by_sql(sql)
public function find_by_sql($sql) {
return $this->connection->execute($sql)->fetchAll('assoc');
}
# .find('all', :condition=>[], :order->[], :select=>[])
public function find_all($conditions=[], $order=[], $select=[]) {
$query = $this->find('all', ['conditions'=>$conditions, 'order'=>$order]);
if (!empty($select)) $query = $query->select($select);
return $query->toList();
}
# .find('first', :condition=>[], :order->[])
public function find_first($conditions=[], $order=[]) {
return $this->find('all', ['conditions'=>$conditions, 'order'=>$order])->first();
}
# .exists(:conditions=>[])
public function exists($conditions) : bool{
return parent::exists($conditions);
}
}
ビューヘルパー
Railsと同名関数を使える共通ヘルパーを作成
/src/View/Helper/RailsFormHelper.php
namespace App\View\Helper;
use Cake\View\Helper\FormHelper;
use Cake\View\StringTemplateTrait;
class RailsFormHelper extends FormHelper
{
use StringTemplateTrait;
protected $_defaultConfig = [
'templates' => [
'input' => '<input type="{{type}}" name="{{name}}" id="{{name}}"{{attrs}}/>',
]
];
function hidden_field_tag($fieldName, $value) {
return $this->hidden($fieldName, ['id'=>$fieldName, 'value'=>$value]);
}
}
/src/View/Helper/RailsHtmlHelper.php
namespace App\View\Helper;
use Cake\View\Helper\HtmlHelper;
use Cake\View\StringTemplateTrait;
class RailsHtmlHelper extends HtmlHelper
{
protected $_defaultConfig = [
'templates' => [
'image' => '<img src="{{url}}"{{attrs}}/>',
]
];
// <?= image_tag("status1.png", :title => "保留する", :style => "cursor:pointer;", :onclick => "status1_click();")
function image_tag($path, $options = []) {
return $this->image($path, $options);
}
}
/src/View/Helper/RailsUrlHelper.php
namespace App\View\Helper;
use Cake\View\Helper\UrlHelper;
class RailsUrlHelper extends UrlHelper
{
function url_for($options = []) {
$path = $options['controller'].'/'.$options['action'];
$params = [];
foreach ($options as $key => $val) {
if (in_array($key, ['controller', 'action', 'anchor'])) {
continue;
}
$params[] = $key.'='.$val;
}
if (!empty($params)) {
$path .= '?'.implode('&', $params);
}
if (isset($params['anchor'])) {
$path .= '#'.$params['anchor'];
}
return $this->build($path);
}
}
コントローラからビューで使用する変数の設定
/app/controllers/test_controller.rb
class TestController < ActionController::Base
def index
@hoge = 'aaa'
end
end
/app/views/layouts/test/index.html.erb
<%= @hoge %>
/src/Controller/TestController.php
class TestController < AppController
{
public function index()
{
$this->set('hoge', 'aaa');
}
}
/templates/Test/index.php
<?= $hoge ?>
I18n
config/locales/ja.yml
ja:
fruits:
apple: "りんご"
orange: "みかん"
<%= t("fruits.apple") %>
<%= t("fruits.orange") %>
CakePHPでyamlを使用できるようにする
/src/I18n/Parser/YamlFileParser.php
namespace App\I18n\Parser;
class YamlFileParser
{
public function parse($file)
{
return yaml_parse_file($file);
}
}
Railsと同様にインデントで階層を記述してみたところ、
うまく取得できなかったので、以下のように修正。
(原因については未調査)
/resources/locales/ja_JP/default.yaml
fruits.apple: "りんご"
fruits.orange: "みかん"
<?= __("fruits.apple") ?>
<?= __("fruits.orange") ?>
authenticity_token
パスワードのエンコード・デコード
備考
実際に置き換えた内容についてのみを記述しています。
不足分や必要があれば追記します。