FuelPHPサイド
FuelBeans 36 : Shooting Brake 8
最後の仕上げです。ブログ投稿(ポスト)の更新と一覧表示です。
では、まとめてコードを紹介します。
まず、一覧表示のビュー、post/index.phpです。
<table>
<tr style="font-weight: bolder;">
<td>タイトル</td>
<td>カテゴリー</td>
<td>本文</td>
<?php if ($logined) : ?>
<td>編集</td>
<td>削除</td>
<?php endif; ?>
</tr>
<?php foreach ($data as $key => $item) : ?>
<tr>
<td title="<?php echo $key; ?>"><?php echo $item['short_title']; ?></td>
<td title="<?php echo $item['category'] ?>"><?php echo $item['short_category']; ?></td>
<td title="<?php echo $item['content'] ?>"><?php echo $item['short_content']; ?></td>
<?php if ($logined) : ?>
<td><?php echo Html::anchor('/blog/update/'.$item['id'], '更新'); ?></td>
<td><?php echo Html::anchor('/blog/delete/'.$item['id'], '削除'); ?></td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</table>
続いて、更新処理のビュー、post/update.phpです。
<?php echo Form::open(Uri::current()); ?>
<?php echo Form::label('タイトル : ', 'title'); ?>
<?php echo Form::input('title', Input::post('title') ? : $title, array('size' => 50)); ?>
<?php echo Form::label('説明 : ', 'content'); ?>
<?php
echo Form::textarea('content',
Input::post('content') ? : $content, array('rows' => 20, 'cols' => 80));
?>
<?php echo Form::select('category', $selected, $category); ?>
<?php echo Form::submit('submit', '登録'); ?>
<?php echo Form::close(); ?>
postモデルの最終型です。
<?php
class Controller_Post extends Controller_Admin
{
/**
* action_add() ポスト追加ページ
*
* @return Fuel\Core\View
*/
function action_add()
{
if (Input::method() == 'POST')
{
/* @var $val Fuel\Core\Validation */
$val = Validation::forge();
$val->add('title', 'タイトル')
->add_rule('trim')
->add_rule('required')
->add_rule('max_length', 50)
;
$val->add('content', '本文')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 5)
->add_rule('max_length', 255)
;
// 本来はカテゴリーの存在チェックを行う。
// ユーザーからのインプットをそのまま信用しない
$category_id = Input::post('category');
// バリデーション
if ($val->run())
{
// ポスト追加
if (!Model_Post::add($val->validated('title'), $val->validated('content'), Input::post('category')))
{
// 追加失敗
Session::set_flash('error', 'ポストを追加できませんでした。時間をおいて、後ほどお試しください。');
}
else
{
unset($_POST['title']);
unset($_POST['content']);
unset($_POST['category']);
Session::set_flash('message', 'ポストを追加しました。');
}
}
else
{
// バリデーションNG
Session::set_flash('notice', $val->errors());
} // バリデーション終了
} // ポストチェック終了
// カテゴリーの取得
$category = Model_Category::get_all();
if ($category)
{
$category_list = array();
$first_item = false;
$selected = false;
// Form::select()出力用に編集
foreach ($category as $i)
{
$category_list[$i['id']] = $i['name'];
$selected = $i['name'] == '未カテゴリー' ? $i['id'] : $selected;
$first_item = $first_item ? : $i['id'];
}
$view_add = View::forge('post/add');
$view_add->category = $category_list;
// POSTされていれば、その値をセレクト状態にする。
// POSTされていなければ、'未カテゴリー'をセレクト、
// '未カテゴリー'が存在しなければ、最初のカテゴリーをセレクト
$view_add->selected = Input::post('category', false) ? : ($selected ? : $first_item);
$view = $this->set_title('ポスト追加')
->set_content($view_add)
->get_view();
return $view;
}
else
{
set_flash('notice', 'カテゴリーが取得できません。');
Response::redirect('/');
}
}
/**
* ポスト更新
*
* @param string $id
* @return Fuel\Core\View
* @throws HttpNotFoundException
*/
function action_update($id)
{
// idの存在を確認
$result = Model_Post::get($id);
if (!$result)
{
// 404ページ表示
throw new HttpNotFoundException;
}
if (Input::method() == 'POST')
{
/* @var $val Fuel\Core\Validation */
$val = Validation::forge();
$val->add('title', 'タイトル')
->add_rule('trim')
->add_rule('required')
->add_rule('max_length', 50)
;
$val->add('content', '本文')
->add_rule('trim')
->add_rule('required')
->add_rule('min_length', 5)
->add_rule('max_length', 255)
;
// バリデーション
if ($val->run())
{
// バリデーションOK
// 入力項目が変更された時のみ更新をかける
if (Input::post('title') != $result['title'] or Input::post('content') != $result['content'] or Input::post('category') != $result['category'])
{
// ポスト更新
if (Model_Post::update($id, $val->validated('title'), $val->validated('content'),
Input::post('category')))
{
Session::set_flash('message', 'ポストを更新しました。');
}
else
{
// 更新失敗
Session::set_flash('error', 'ポストを更新できませんでした。時間をおいて、後ほどお試しください。');
}
}
else
{
Session::set_flash('notice', '項目は変更されていません。');
}
}
else
{
// バリデーションNG
Session::set_flash('notice', $val->errors());
} // バリデーション終了
} // ポストチェック終了
// カテゴリーの取得
$category = Model_Category::get_all();
if ($category)
{
$category_list = array();
// Form::select()出力用に編集
foreach ($category as $i)
{
$category_list[$i['id']] = $i['name'];
}
$update_view = View::forge('post/update');
$update_view->title = $result['title'];
$update_view->content = $result['content'];
$update_view->category = $category_list;
// POSTされていれば、その値をセレクト状態にする。
// POSTされていなければ、そのポストのカテゴリーID
$update_view->selected = Input::post('category', false) ? : $result['category_id'];
$view = $this->set_title('ポスト更新')
->set_content($update_view)
->get_view();
return $view;
}
else
{
set_flash('notice', 'カテゴリーが取得できません。');
Response::redirect('/');
}
}
/**
* ポスト削除
*
* @param int $id
* @throws HttpNotFoundException
*/
public static function action_delete($id)
{
// idの存在を確認
$result = Model_Post::get($id);
if (!$result)
{
// 404ページ表示
throw new HttpNotFoundException;
}
if (Model_Post::delete($id))
{
Session::set_flash('message', 'ポストを削除しました。');
}
else
{
Session::set_flash('error', 'ポストの削除に失敗しました。');
}
Response::redirect('/');
}
}
コントローラー、最初はログインが必要なページアクションが集まっている、post.phpです。
<?php
class Model_Post extends Model
{
const DB_NAME = 'post';
/**
* 新ポスト追加
*
* @param string $title
* @param string $content
* @param string $category
* @return mix 挿入時行数(int)、挿入されない場合にはfalse
*/
public static function add($title, $content, $category)
{
if ($title == null or $content == null or $category == null)
{
return false;
}
list($insert_id, $inserted_cnt) = DB::insert(self::DB_NAME)
->set(array(
'title' => $title,
'content' => $content,
'category_id' => $category,
'created' => time(),
))
->execute();
return isset($inserted_cnt) ? : false;
}
/**
* IDを元に1件ゲットだぜ
*
* 仮に、複数件一致しても1件分のデーターのみを返す
*
* @param int $id
* @return mix SELECT結果1件分、エラー時false
*/
public static function get($id)
{
// FuelPHPのSQLビルダーは、原則全部エスケープするため
// 不必要であるが、念のため
if (!is_numeric($id))
{
return false;
}
$result = DB::select()
->from(self::DB_NAME)
->where('id', $id)
->execute()
->current(); // 1件分
return isset($result) ? $result : false;
}
/**
* ポスト更新
*
* 戻り値に注意。idが一致しない時も、id、name、content, category_id
* 全く同一値で更新した時も0になる。
*
* @param int $id
* @param string $title
* @param string $content
* @param string $category_id
* @return mix 更新時件数, エラー時false
*/
public static function update($id, $title, $content, $category_id)
{
// FuelPHPのSQLビルダーは、原則全部エスケープするため
// 不必要であるが、念のため
if (!is_numeric($id))
{
return false;
}
$updated_cnt = DB::update(self::DB_NAME)
->set(array(
'title' => $title,
'content' => $content,
'category_id' => $category_id,
))
->where('id', $id)
->execute();
return $updated_cnt;
}
/**
* ポスト削除
*
* @param int $id
* @return mix 削除時件数(int)、エラー時false
*/
public static function delete($id)
{
// FuelPHPのSQLビルダーは、原則全部エスケープするため
// 不必要であるが、念のため
if (!is_numeric($id))
{
return false;
}
$deleted_cnt = DB::delete(self::DB_NAME)
->where('id', $id)
->execute();
return $deleted_cnt;
}
/**
* ポスト全件取得
*
* @return mix 成功時SELECT結果、エラー時および0件時false
*/
public static function get_all()
{
$result =
DB::select()
->from(self::DB_NAME)
->execute();
return isset($result) ? $result : false;
}
}
最後に、ログイン不必要で閲覧できるページアクションのhome.phpです。
<?php
class Controller_Home extends Controller_base
{
/**
* ポスト一覧表示
*
* @return Fuel\Core\View
*/
function action_index()
{
$category = Model_Category::get_all();
if (!$category)
{
Session::set_flash('notice', 'カテゴリーが存在しません。');
Response::redirect('/'); // これでは転送がループしてしまいます。どうするのが良いと思いますか?
}
$category = $category->as_array('id', 'name');
$result = Model_Post::get_all();
if ($result)
{
$data = array();
foreach ($result as $i)
{
$data[$i['title']] = array(
'id' => $i['id'],
'category' => $category[$i['category_id']],
'content' => $i['content'],
'short_title' => Str::truncate($i['title'], 10, '…'),
'short_category' => Str::truncate($category[$i['category_id']], 10, '…'),
'short_content' => Str::truncate($i['content'], 30, '…'),
);
}
ksort($data, SORT_LOCALE_STRING);
}
else
{
$data = false;
}
$view_index = View::forge('post/index');
$view_index->data = $data;
$view_index->logined = Auth::check();
$view = $this->set_title('ポスト一覧')
->set_content($view_index)
->get_view();
return $view;
}
/**
* ポスト1件表示
*
* @param int $id
* @return Fuel\Core\View
* @throws HttpNotFoundException
*/
function action_show($id)
{
// idの存在を確認
$result = Model_Post::get($id);
if (!$result)
{
// 404ページ表示
throw new HttpNotFoundException;
}
// カテゴリー名取得
$category = Model_Category::get($result['category_id']);
$category = $category ? $category['name'] : '未カテゴリー';
$show_view = View::forge('post/show');
$show_view->id = $id;
$show_view->title = $result['title'];
$show_view->content = $result['content'];
$show_view->category = $category;
// リンクの表示を切り分けるため
// ログイン情報をビューへ渡す
$show_view->logined = Auth::check();
$view = $this->set_title('ポスト表示')
->set_content($show_view)
->get_view();
return $view;
}
}
コードを紹介しましたが、各機能のリンクは完全ではありません。例えば、どのページからもホームへ移動するリンクは入っていません。新規項目の追加も入っていません。
機能的にも不完全です。コメントで、考えるべきところは、コードを入れず、残しております。
更に、大きなバグも残しています。カテゴリーの削除をする際、既にポストでそのカテゴリーが付けられていたら、どうなるでしょう?また、こうした場合にどう処理するのが適当でしょうか?未カテゴリーにするのがいいのでしょうか?それとも、ユーザーに確認を取るのが親切なのでしょうか?
これらの問題を実際にコーディングしてくださいとは言いません。ブログでしたらWordpressをお勧めします。
もし、あなたが小さくてもシステム開発の経験がないのならば、一分考えるだけでも、得るものは大きいでしょう。自分の頭で考えて、今度は実際に役に立つプログラミングを楽しんでください。
テスト不十分、コードももうちょっと書き直したいですが、チュートリアルのコードにこれ以上手を加えても仕方が無いので、この状態のまま、ダウンロード可能にしておきます。物好きな方は、こちらからどうぞ。
| FuelBeans 35 : Shooting Brake 7< 前 |
|---|
| < 前 |
|---|