owned this note
owned this note
Published
Linked with GitHub
# PHP独自フレームワークチュートリアル
## 1.MAMPをインストールする
- https://techacademy.jp/magazine/5102
## 2.MAMPの設定をする
![](https://i.imgur.com/rwstmnm.png)
- PHPのバージョンを7.4.9に設定する。Apacheを選択する。
![](https://i.imgur.com/ERA5q5p.png)
- MAMPのhtdocs配下に「php_tutorial」ディレクトリを作成してください。
![](https://i.imgur.com/DGMygHu.png)
- MAMPを立ち上げよう
## 3.PHPでブログを作ろう
### 3.1 投稿した内容を画面を表示しよう。
任意のディレクトリにpost.phpを作成してください。
次に投稿された内容を表示させてみます。今回はフォームが記述しているHTMLファイル自身にPOSTを使って送ってみます。
```htmlmixed=
<html>
<head><title>PHP TEST</title></head>
<body>
<p>掲示板</p>
<form method="POST" action="<?php print($_SERVER['PHP_SELF']) ?>">
<input type="text" name="personal_name"><br><br>
<textarea name="contents" rows="8" cols="40">
</textarea><br><br>
<input type="submit" name="btn1" value="投稿する">
</form>
<?php
$personal_name = $_POST['personal_name'];
$contents = $_POST['contents'];
print('<p>投稿者:'.$personal_name.'</p>');
print('<p>内容:</p>');
print('<p>'.$contents.'</p>');
?>
</body>
</html>
```
### 3.2 MVCのブログを作ろう
- この機能を実装してください。
#### 機能一覧
- ユーザ登録機能
- ログイン機能
- ログアウト機能
- 記事登録機能
- 記事一覧機能
- カテゴリ登録機能
- カテゴリ一覧機能
#### MVCとは?
![](https://i.imgur.com/hdPV7CW.png)
https://asobo.hatenablog.jp/entry/2016/01/17/120310
#### MVCの概念とは?
MVCの概念とは「機能によってプログラムの中身を分けて記述する」ことです。MVCは機能ではなく考え方で、アプリケーション全体のコードを管理するための考え方です。
役割ごとに意味のある単位で機能をセットすると管理しやすいという考え方で、M=モデル、V=ビュー、C=コントローラーに分割しセットします。
複数人で開発を行った場合に、どこに何があるのかすぐ分かるように用いる型紙がMVCです。
MVCはモデル、ビュー、コントローラー、それぞれの機能の頭文字です。モデルはシステム内の目的となる処理をします。データやビジネスロジックを処理する、システムの本体となる部分です。
ビューは実際に表示したり、入力したデータを処理したりする、ユーザーインターフェイスの部分を行います。
コントローラーは、モデルとビューを制御する役割を担います。
#### MVCの役割1: モデル
MVCのモデルとはシステムの本体、根幹の大切な部分です。データベースとデータのやり取りを行う、データベースから取得したデータをプログラムで使いやすいように取り出し変換する、といった役割を司ります。
データ管理の役割の責任を持つパーツです。データ管理のコードをモデルに集中させることで、不具合が起こったときにモデルに問題があることがすぐにわかります。
例
データを関連づけ検証するなど、繊細な処理を行います。
例としては、日付機能やデータの保存、計算や、ユーザーインターフェイスに通知するという役割もあります。
ユーザーアクセスにより促された処理をデータベースにアクセスし、操作・参照して必要な値に取り出し、または変化して、インターフェイスに通知するのがモデルの役割です。
#### MVCの役割2:ビュー
MVCのビューとはユーザーインターフェイスの部分を担当します。ユーザーインターフェイスとはユーザーが直接見る画面のことで、入力した機能の処理を行います。
使用者(ユーザー)とサイトとのインターフェイス(接点)でもあり、レイアウトやメニュー、ボタンの操作性など、画面表示やボタンの操作性もビューの役割です。表示周りの責任を持ちます。
例
ビューの役割は、データを表示させたりする出力部分を担当します。HTMLを「動的に生成する」という役目を負います。
例えば、TwitterなどのSNSなどは、最新の情報を画面に表示させます。アクセスするたびにデータベースから最新記事を取得し、トップページに表示するという処理を行います。
このような動きのある部分を処理するのがビューの役割です。
#### MVCの役割3: コントローラー
MVCのコントローラーとはユーザーの入力に基づき、モデルとビューを制御する役目を担う部分です。ユーザーが入力した情報に基づいて、モデルへデータを取り出す指示を、ビューにはモデルで取り出したデータを元に画面を表示する指示を出します。
わかりやすくいうとコントローラーはモデルとビューの橋渡し的な役目を担い、繋ぎとしての役目に徹します。
例
コントローラーの役目は、クライアントとビュー、モデルとの橋渡し役です。
クライアントからのリクエストに応じた処理を行う際、必要に応じてモデルからデータの受け渡しを行い、ビューに表示する画面の処理を促す、という仲介役がコントローラーの役目です。
データベースに送られてきたクライアントが入力した情報をビューへ渡し、画面を表示します。クライアントのボタン操作による情報でモデルに処理を促します。
#### MVCのメリット
- 1.効率が上がる
MVCとは専門性の高い業務を3つの役割にそれぞれに振り分けています。役割や機能別でコードを分けることにより、開発するときも、変更する時も、保守するときも、「このコードはどこの関連か?」と迷うことがなくなります。
- 2.保守性を確保できる
MVCは機能ごとに区分されているため保守性が確保されています。各モジュールで役割分担していることにより、保守性が上がり、品質も向上します。
- 3.コードを再利用しやすい
MVCの考え方で設計すると、コードを再利用できることが多いです。特に、モデルのコードは比較的簡単に再利用ができます。
- 4.機能を分割できる
MVCは、機能を分割してそれぞれ専門性の高い仕事を振り分けるため、独立性が高く、変更などの対応も柔軟に行えます。
#### MVCのデメリット
- 1.制約がかかる
MVCはオブジェクトを分けてコードを記述していきます。オブジェクトの複数作成が必要で、処理に時間がかかります。また、オブジェクト間のやりとりに制約がかかり、コードを柔軟に記述できなくなるというデメリットが発生します。
- 2.分割に時間がかかる
MVCは機能を分割してコードを記述します。機能にあった役割分担ができていないと、MVCのメリットが享受できなくなります。
- 参考:
https://www.fenet.jp/infla/column/network/mvc%E3%81%A8%E3%81%AF%EF%BC%9Fmvc%E3%81%AE%E6%A6%82%E5%BF%B5%E3%82%84%E5%BD%B9%E5%89%B23%E3%81%A4%E3%81%A8%E5%85%B7%E4%BD%93%E4%BE%8B%E3%82%92%E7%B4%B9%E4%BB%8B%EF%BD%9C%E3%83%A1%E3%83%AA%E3%83%83/
- PHPでMVCを理解しよう。
![](https://i.imgur.com/35nxKpx.png)
![](https://i.imgur.com/W3Pq7V3.png)
#### データベースのテーブルのリレーションとは?
リレーションとは、テーブルとテーブルを繋ぐ共通のキー(key)となります。Excelで言うところの別シートのデータを紐づける意味と同じです。
例えば学校を例に例えるならば、「学校」「授業」「担任」「生徒」がいます。学校にそれぞれIDを持っていれば、そのIDをキーとして「授業」「担任」「生徒」を紐づけると言うのがリレーションになります。 では、リレーションはどのような種類があるかを紹介していきたいと思います。
リレーションの種類としては以下の3パターンがあります。nは多とも言います。
・1:1(一対一)
・1:n(一対多)
・n:n(多対多)
![](https://i.imgur.com/TSvITZw.png)
![](https://i.imgur.com/X9Ricm2.png)
![](https://i.imgur.com/5cq0Bd9.png)
![](https://i.imgur.com/X4YPE3U.png)
- 参考:
https://akiyoko.hatenablog.jp/entry/2016/07/31/232754
https://coosy.co.jp/blog/web-database-base/
#### phpmyAdminでSQLを流し込んでください。
http://localhost:8888/phpMyAdmin/?lang=ja
![](https://i.imgur.com/KoOpuSe.png)
```sql=
CREATE DATABASE blog CHARACTER SET utf8mb4;
use blog;
CREATE TABLE posts (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
title TEXT,
content TEXT,
category_id INT,
date TIMESTAMP
);
CREATE TABLE categories (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
name TEXT
);
CREATE TABLE users (
user_id INT( 5 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
name VARCHAR( 25 ) NOT NULL ,
email VARCHAR( 35 ) NOT NULL ,
pass VARCHAR( 60 ) NOT NULL ,
UNIQUE (email)
);
```
![](https://i.imgur.com/bNvSRDw.png)
![](https://i.imgur.com/WcrjVYr.png)
- 下記のディレクトリ構成を作成しよう。
![](https://i.imgur.com/CBZ8DP5.png)
/Applications/MAMP/htdocs/
に
php_tutorial
フォルダを作成する
### 3.2.1 記事の一覧表示まで実装しましょう!
- index.phpをルートディレクトリに作成しよう。
/index.php
```php
<?php
ini_set('display_errors', "On");
require_once ('./route/RequestUrl.php');
require_once ('./controller/PostController.php');
$request = new RequestUrl();
$url = $request->getPathInfo();
// routing
switch ($url){
case '/':
case '/post':
$postController = new PostController();
$postController->indexAction();
break;
}
```
- routeディレクトリ配下にroutingを作成しよう
/route/RequestUrl.php
```php=
<?php
/**
* Class RequestUrl
*/
class RequestUrl{
/**
* @return string
*/
public function getBaseUrl(){
$script_name = $_SERVER['SCRIPT_NAME'];
$request_uri = $_SERVER['REQUEST_URI'];
if(0 === strpos($request_uri, $script_name)){
//フロントコントローラがURLに含まれる場合
return $script_name;
}elseif(0 === strpos($request_uri, dirname($script_name))){
//フロントコントローラが省略されている場合
return rtrim(dirname($script_name), '/');
}
return '';
}
/**
* @return string
*/
public function getPathInfo(){
$base_url = $this->getBaseUrl();
$request_uri = $_SERVER['REQUEST_URI'];
if(false !== ($pos = strpos($request_uri, '?'))){
//GETパラメータを削除
$request_uri = substr($request_uri, 0, $pos);
}
//REQUEST_URIからベースURLを取り除く
$path_info = (string)substr($request_uri, strlen($base_url));
return $path_info;
}
}
```
- public配下にファイルを展開してください。 https://drive.google.com/file/d/1tT4fBjyxXOBvX7xepQ3PLqYLbDa8Cw-9/view?usp=sharing
- publicディレクトリのviewディレクトリ配下にpost_index.phpを作成しよう
/public/view/post_index.php
```htmlmixed=
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Clean Blog - Start Bootstrap Theme</title>
<!-- Bootstrap core CSS -->
<link href="./public/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom fonts for this template -->
<link href="./public/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<!-- Custom styles for this template -->
<link href="./public/css/clean-blog.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container">
<a class="navbar-brand" href="./">Re:Build</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="fa fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="./">Home</a>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px; margin-right: 20px;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
ブログ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="./post/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
カテゴリ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="./category/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="./logout">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Header -->
<header class="masthead" style="background-image: url('./public/img/home-bg.jpg')">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="site-heading">
<h1>Tech Blog</h1>
<span class="subheading">challenge</span>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<?php foreach ($posts as $post) { ?>
<div class="post-preview">
<a href="post.html">
<h2 class="post-title">
<?php echo $post['title'] ?>
</h2>
<h3 class="post-subtitle">
<?php echo nl2br($post['content']) ?>
</h3>
<i class="far fa-bookmark"></i>
<?php echo nl2br($post['category_name']) ?><br>
<i class="fas fa-tags"></i>
</a>
<p class="post-meta">Posted <?php echo $post['date'] ?>
</div>
<?php } ?>
<hr>
<!-- Pager -->
<div class="clearfix">
<a class="btn btn-primary float-right" href="#">Older Posts →</a>
</div>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<ul class="list-inline text-center">
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-facebook fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">Copyright © Your Website 2018</p>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="./public/vendor/jquery/jquery.min.js"></script>
<script src="./public/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Custom scripts for this template -->
<script src="../js/clean-blog.min.js"></script>
</body>
</html>
```
- controllerディレクトリ配下にControllerとPostControllerを作成しよう
/controller/Controller.php
```php=
<?php
session_start();
/**
* Class Controller
*/
abstract class Controller
{
/**
* Controller constructor.
*/
function __construct(){
// 後で処理を書く
}
}
```
/controller/PostController.php
```php=
<?php
ini_set('display_errors', "On");
require_once ('Controller.php');
require_once ('./model/PostModel.php');
require_once('./model/PostModel.php');
/**
* Class PostController
*/
class PostController extends Controller
{
/**
* PostController constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* 記事一覧
*/
public function indexAction()
{
$postModel = new PostModel();
$posts = $postModel->get();
require("./public/view/post_index.php");
}
}
```
- modelディレクトリ配下にModelを作成する
/model/Model.php
```php=
<?php
require_once ('conf/Database.php');
/**
* Class Model
*/
abstract class Model
{
/**
* @var PDO
*/
private $pdo;
/**
* Model constructor.
*/
public function __construct()
{
try {
$this->pdo = new PDO(
sprintf('mysql:host=%s;dbname=%s;charset=utf8',DataBase::HOST_NAME,DataBase::DB_NAME),
DataBase::DB_USER,
DataBase::DB_PASSWORD,
[
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch (PDOException $e) {
exit('データベース接続失敗。' . $e->getMessage());
}
}
/**
* @return PDO
*/
public function getPdo()
{
return $this->pdo;
}
}
```
/model/PostModel.php
```php=
<?php
require_once('Model.php');
/**
* Class PostModel
*/
class PostModel extends Model
{
/**
* @var PDO
*/
private $pdo;
/**
* PostModel constructor.
*/
function __construct()
{
parent::__construct();
$this->pdo = parent::getPdo();
}
/**
* @return array
*/
public function get()
{
$st = $this->pdo->query("SELECT
posts.id,
posts.title,
posts.content,
posts.category_id,
posts.date,
categories.name as category_name
FROM posts INNER JOIN categories
ON posts.category_id = categories.id;");
try {
$posts = $st->fetchAll();
} catch (PDOException $e) {
exit('取得失敗' . $e->getMessage());
}
return $posts;
}
}
```
- modelのconfディレクトリ配下に配置
/model/conf/Database.php
```php=
<?php
/**
* Class DataBase
*/
class DataBase
{
/**
* ホスト名
*/
const HOST_NAME = 'localhost';
/**
* DB名
*/
const DB_NAME = 'blog';
/**
* DBのユーザ名
*/
const DB_USER = 'root';
/**
* DBのパスワード
*/
const DB_PASSWORD = 'root';
}
```
- .htaccessを配置する
/.htaccess
```
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
```
- 記事をSQLで登録する
```sql=
INSERT INTO posts (title, content, category_id) VALUES ('test', 'testetest', 1);
INSERT INTO categories (name) VALUES ('夏');
```
![](https://i.imgur.com/yfJfIvP.png)
![](https://i.imgur.com/hW13Cko.png)
- 下記にアクセスするとトップページが表示される事
- http://localhost:8888/php_tutorial
![](https://i.imgur.com/gk13jtb.jpg)
### 3.2.2 ログイン・ユーザ登録機能を作る
- controllerディレクトリ配下にControllerを修正する。ログインチェックをいれる
/controller/Controller.php
```php=
<?php
session_start();
/**
* Class Controller
*/
abstract class Controller
{
/**
* Controller constructor.
*/
function __construct(){
if(empty($_SESSION['user'])) {
header("Location: ./login");
exit;
}
}
}
```
- Controller配下にValidation.phpを作成する
/controller/Validation.php
```php=
<?php
ini_set('display_errors', 1);
class Validation {
//未入力チェック
function blankCheck($value)
{
return empty($value);
}
// メール形式チェック
function emailCheck($value)
{
if(filter_var($value, FILTER_VALIDATE_EMAIL)) {
return false;
} else {
return true;
}
}
// 文字数制限(4〜10文字)
function length4_10Check($value)
{
if(($value < 4) or ($value > 10)) {
return true;
} else {
return false;
}
}
// 文字数制限(〜50文字)
function length50Check($value)
{
if($value > 50) {
return true;
} else {
return false;
}
}
// 使用不可文字チェック
function typeCheck($value)
{
if (!preg_match("/^[A-Za-z0-9_]+$/",$value)) {
return true;
} else {
return false;
}
}
}
```
- user_add.phpをview配下に作成する
/public/view/user_add.php
```htmlmixed=
<?php
// エラーを出力する
ini_set('display_errors', "On");
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Clean Blog - Start Bootstrap Theme</title>
<!-- Bootstrap core CSS -->
<link href="../public/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom fonts for this template -->
<link href="../public/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet'
type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800'
rel='stylesheet' type='text/css'>
<!-- Custom styles for this template -->
<link href="../public/css/clean-blog.min.css" rel="stylesheet">
<style>
.error {
color: red;
}
</style>
</head>
<body>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<form method="post" action="./add">
<h1>会員登録フォーム</h1>
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="ユーザー名" required/>
<p class="error"><?php echo isset($errors['username']) ? $errors['username'] :''; ?></p>
</div>
<div class="form-group">
<input type="email" class="form-control" name="email" placeholder="メールアドレス" required/>
<p class="error"><?php echo isset($errors['email']) ? $errors['email'] :''; ?></p>
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="パスワード" required/>
<p class="error"><?php echo isset($errors['password']) ? $errors['password'] :''; ?></p>
</div>
<p><input class="btn btn-primary" name="submit" type="submit" value="会員登録"></p>
<a href="../login">ログインはこちら</a>
</form>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<p class="copyright text-muted">Copyright © Your Website 2018</p>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="../public/vendor/jquery/jquery.min.js"></script>
<script src="../public/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Custom scripts for this template -->
<script src="../public/js/clean-blog.min.js"></script>
</body>
</html>
```
- login.phpをview配下に作成する
/public/view/login.php
```htmlmixed=
<?php
ini_set('display_errors', 1);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Clean Blog - Start Bootstrap Theme</title>
<!-- Bootstrap core CSS -->
<link href="./public/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom fonts for this template -->
<link href="./public/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet'
type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800'
rel='stylesheet' type='text/css'>
<link href="./public/css/clean-blog.min.css" rel="stylesheet">
<link href="./public/css/login.css" rel="stylesheet">
</head>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<div class="container">
<div class="card card-container">
<img id="profile-img" class="profile-img-card" src="//ssl.gstatic.com/accounts/ui/avatar_2x.png" />
<p id="profile-name" class="profile-name-card"></p>
<form method="post" action="./login">
<h2>ログイン</h2>
<div class="form-group">
<input type="email" class="form-control" name="email" placeholder="メールアドレス" required />
<p class="error"><?php echo isset($errors['email']) ? $errors['email'] :''; ?></p>
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="パスワード" required />
<p class="error"><?php echo isset($errors['password']) ? $errors['password'] :''; ?></p>
</div>
<p><input class="btn btn-primary" name="submit" type="submit" value="ログイン"></p>
<a href="./user/add">会員登録はこちら</a>
</form>
</div><!-- /card-container -->
</div><!-- /container -->
```
- publicのcss配下にlogin.cssを作成する
/public/css.login.css
```css=
.error-message {
color: red;
}
/*
* Specific styles of signin component
*/
/*
* General styles
*/
body, html {
height: 100%;
background-repeat: no-repeat;
background-image: linear-gradient(rgb(104, 145, 162), rgb(12, 97, 33));
}
.card-container.card {
max-width: 350px;
padding: 40px 40px;
}
.btn {
font-weight: 700;
height: 36px;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: default;
}
/*
* Card component
*/
.card {
background-color: #F7F7F7;
/* just in case there no content*/
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;
/* shadows and rounded borders */
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}
.profile-img-card {
width: 96px;
height: 96px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
/*
* Form styles
*/
.profile-name-card {
font-size: 16px;
font-weight: bold;
text-align: center;
margin: 10px 0 0;
min-height: 1em;
}
.reauth-email {
display: block;
color: #404040;
line-height: 2;
margin-bottom: 10px;
font-size: 14px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.form-signin #inputEmail,
.form-signin #inputPassword {
direction: ltr;
height: 44px;
font-size: 16px;
}
.form-signin input[type=email],
.form-signin input[type=password],
.form-signin input[type=text],
.form-signin button {
width: 100%;
display: block;
margin-bottom: 10px;
z-index: 1;
position: relative;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.form-signin .form-control:focus {
border-color: rgb(104, 145, 162);
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgb(104, 145, 162);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgb(104, 145, 162);
}
.btn.btn-signin {
/*background-color: #4d90fe; */
background-color: rgb(104, 145, 162);
/* background-color: linear-gradient(rgb(104, 145, 162), rgb(12, 97, 33));*/
padding: 0px;
font-weight: 700;
font-size: 14px;
height: 36px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
border: none;
-o-transition: all 0.218s;
-moz-transition: all 0.218s;
-webkit-transition: all 0.218s;
transition: all 0.218s;
}
.btn.btn-signin:hover,
.btn.btn-signin:active,
.btn.btn-signin:focus {
background-color: rgb(12, 97, 33);
}
.forgot-password {
color: rgb(104, 145, 162);
}
.forgot-password:hover,
.forgot-password:active,
.forgot-password:focus{
color: rgb(12, 97, 33);
}
```
- Controller配下にUserControllerクラスを作成する
/controller/UserController.php
```php=
<?php
require_once('./model/UserModel.php');
require_once('Validation.php');
/**
* Class UserController
*/
class UserController
{
/**
* UserController constructor.
*/
public function __construct()
{
}
/**
* ユーザ登録
*/
public function addAction()
{
if (@$_POST['submit']) {
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];
// バリデーションチェック
$errors = $this->validateAdd($_POST);
// バリデーションエラーがない場合
if (count($errors) === 0) {
$userModel = new UserModel();
$userModel->add($username, $email, $password);
header("Location: ../login");
exit();
}
}
require("./public/view/user_add.php");
}
/**
* ログイン
*/
public function loginAction()
{
if (!empty($_POST)) {
$email = $_POST['email'];
$password = $_POST['password'];
// バリデーションチェック
$errors = $this->validateLogin($_POST);
// バリデーションエラーがない場合
if (count($errors) === 0) {
$userModel = new UserModel();
$user = $userModel->login($email, $password);
if (count($user) > 0) {
$_SESSION['user'] = $user;
header("Location: ./");
} else {
$errors['email'] = 'メールアドレスまたはパスワードが一致しません。';
$errors['password'] = 'メールアドレスまたはパスワードが一致しません。';
}
}
}
require("./public/view/login.php");
}
/**
* ログアウト
*/
public function logoutAction()
{
// セッションを削除
if (isset($_SESSION)) {
$_SESSION = array();
}
// セッションクッキーを削除
if (isset($_COOKIE['cookie'])) {
setcookie('cookie', '', time(), 1800, '/');
}
session_destroy();
require("./public/view/login.php");
}
/**
* @param $name
* @return array
*/
private function validateLogin($input)
{
$errors = [];
$validation = new Validation();
foreach ($input as $key => $value) {
if ($validation->blankCheck($input[$key])) {
switch ($key) {
case 'email':
$errors[$key] = 'メールアドレスを入力してください';
break;
case 'password':
$errors[$key] = 'パスワードを入力してください';
break;
default:
break;
}
}
}
if ($validation->emailCheck($input['email'])) {
$errors['email'] = 'メールアドレスの形式が違います。';
}
return $errors;
}
/**
* @param $name
* @return array
*/
private function validateAdd($input)
{
$errors = [];
$validation = new Validation();
// 未入力チェック
foreach ($_POST as $key => $value) {
if ($validation->blankCheck($input[$key])) {
switch ($key) {
case 'email':
$errors[$key] = 'メールアドレスを入力してください';
break;
case 'password':
$errors[$key] = 'パスワードを入力してください';
break;
case 'name':
$errors[$key] = 'ユーザ名を入力してください';
break;
default:
break;
}
}
}
// 重複チェック
$userModel = new UserModel();
if ($userModel->countEmail($_POST) > 0) {
$errors['email'] = 'そのメールアドレスは既に使用されています。<br>別のメールアドレスを使用してください。';
}
// メールアドレス形式チェック
if ($validation->emailCheck($_POST['email'])) {
$errors['email'] = 'メールアドレスの形式が正しくありません。正しく入力してください。';
}
// 入力文字形式チェック
if ($validation->typeCheck($_POST['password'])) {
$errors['password'] = 'パスワードは半角英数字で入力してください';
}
//文字数制限
if ($validation->length4_10Check(mb_strlen($_POST['username'], 'UTF-8'))) {
$errors['username'] = 'ユーザー名は4〜10文字で入力してください。';
}
if ($validation->length50Check(strlen($_POST['email']))) {
$errors['email'] = 'メールアドレスは50文字以内で入力してください。';
}
if ($validation->length4_10Check(strlen($_POST['password']))) {
$errors['password'] = 'パスワードは4〜10文字で入力してください。';
}
return $errors;
}
}
```
- Model配下にUserModel.phpを配置する
/model/UserModel.php
```php=
<?php
require_once('Model.php');
/**
* Class PostModel
*/
class UserModel extends Model
{
/**
* @var PDO
*/
private $pdo;
/**
* PostModel constructor.
*/
function __construct()
{
parent::__construct();
$this->pdo = parent::getPdo();
}
/**
* @param $username
* @param $email
* @param $password
*/
public function add($username, $email, $password)
{
$stmt = $this->pdo->prepare("INSERT INTO users (name, email, pass) VALUES (:name, :email, :pass)");
$stmt->bindParam(':name', $username, PDO::PARAM_STR);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$password = password_hash($password, PASSWORD_DEFAULT);
$stmt->bindParam(':pass', $password, PDO::PARAM_STR);
try {
$stmt->execute();
} catch (PDOException $e) {
exit('登録失敗' . $e->getMessage());
}
}
/**
* @param array $data
* @return int
*/
public function countEmail(array $data): int
{
$sql = 'SELECT count(*) FROM users WHERE email = :email';
$sth = $this -> pdo -> prepare($sql);
$sth -> bindValue (':email' , $data['email']);
$sth -> execute();
$count = $sth -> fetchColumn();
return $count;
}
/**
* @param $email
* @param $password
* @return array
*/
public function login($email, $password)
{
$stmt = $this->pdo->prepare('SELECT email, pass
FROM users
WHERE email= :email');
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
try {
$stmt->execute();
$user = $stmt->fetchAll();
} catch (PDOException $e) {
exit('登録失敗' . $e->getMessage());
}
// ハッシュ化されたパスワードがマッチするかどうかを確認
if (password_verify($password, $user[0]['pass'])) {
return $user;
}else{
return [];
}
}
}
```
- index.phpに新しいroutingを追加する
/index.php
```php=
<?php
ini_set('display_errors', "On");
require_once ('./route/RequestUrl.php');
require_once ('./controller/PostController.php');
require_once ('./controller/UserController.php');
$request = new RequestUrl();
$url = $request->getPathInfo();
// routing
switch ($url){
case '/':
case '/post':
$postController = new PostController();
$postController->indexAction();
break;
case '/user/add':
$userController = new UserController();
$userController->addAction();
break;
case '/login':
$userController = new UserController();
$userController->loginAction();
break;
case '/logout':
$userController = new UserController();
$userController->logoutAction();
break;
}
```
- 下記のURLにアクセスする
http://localhost:8888/php_tutorial/login
![](https://i.imgur.com/kFIvu1V.png)
http://localhost:8888/php_tutorial/user/add
![](https://i.imgur.com/PBEIBCR.png)
### 3.2.3 カテゴリ登録機能を作る
/controller/CategoryController.php
```php=
<?php
require_once ('Controller.php');
require_once('./model/CategoryModel.php');
/**
* Class CategoryController
*/
class CategoryController extends Controller
{
/**
* CategoryController constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* カテゴリ一覧
*/
public function indexAction()
{
}
/**
* カテゴリ登録
*/
public function addAction()
{
$categoryModel = new CategoryModel();
$categories = $categoryModel->getAll();
$errors = [];
$name = '';
if (@$_POST['submit']) {
$name = $_POST['name'];
// バリデーションチェック
$errors = $this->addValidation($name);
// バリデーションエラーがない場合
if (count($errors) === 0) {
$categoryModel = new CategoryModel();
$categoryModel->add($name);
header('Location: ../');
exit();
}
}
require("./public/view/category_add.php");
}
/**
* @param $name
* @return array
*/
private function addValidation($name){
$errors = [];
if (empty($name)){
$errors['name'] = 'カテゴリ名がありません。<br>';
}
if (mb_strlen($name) > 80){
$errors['name'] = 'カテゴリ名が長すぎます。<br>';
}
return $errors;
}
}
```
- view配下にcategory_add.phpを配置する
/public/view/category_add.php
```htmlmixed=
<?php
// エラーを出力する
ini_set('display_errors', "On");
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Clean Blog - Start Bootstrap Theme</title>
<!-- Bootstrap core CSS -->
<link href="../public/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom fonts for this template -->
<link href="../public/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet'
type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800'
rel='stylesheet' type='text/css'>
<!-- Custom styles for this template -->
<link href="../public/css/clean-blog.min.css" rel="stylesheet">
<style>
.error-message {
color: red;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container">
<a class="navbar-brand" href="./">Re:Build</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="fa fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="../">Home</a>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px; margin-right: 20px;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
ブログ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="../post/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px;" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
カテゴリ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="../category/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="./logout">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Header -->
<header class="masthead" style="background-image: url('../public/img/home-bg.jpg')">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="site-heading">
<h1>Tech Blog</h1>
<span class="subheading">challenge</span>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<form method="post" action="./add">
<div class="post">
<?php if(count($errors) > 0) { ?>
<?php foreach ($errors as $error) { ?>
<p class="error-message"><?php echo $error ?></p>
<?php } ?>
<?php } ?>
<h2>カテゴリ登録</h2>
<p>カテゴリ名</p>
<p><input type="text" name="name" size="40" class="form-control" value="<?php echo $name ?>"></p>
<p><input class="btn btn-primary" name="submit" type="submit" value="投稿"></p>
</div>
</form>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<ul class="list-inline text-center">
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-facebook fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">Copyright © Your Website 2018</p>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="../public/vendor/jquery/jquery.min.js"></script>
<script src="../public/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Custom scripts for this template -->
<script src="../public/js/clean-blog.min.js"></script>
</body>
</html>
```
- Model配下にCategoryModel.phpを作成する
/model/CategoryModel.php
```php=
<?php
require_once('Model.php');
/**
* Class PostModel
*/
class CategoryModel extends Model
{
/**
* @var PDO
*/
private $pdo;
/**
* PostModel constructor.
*/
function __construct()
{
parent::__construct();
$this->pdo = parent::getPdo();
}
public function add($name)
{
$stmt = $this->pdo->prepare("INSERT INTO categories (name) VALUES (:name)");
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
try {
$stmt->execute();
} catch (PDOException $e) {
exit('登録失敗' . $e->getMessage());
}
}
/**
* @return array
*/
public function getAll()
{
$st = $this->pdo->query("SELECT * FROM categories");
try {
$categories = $st->fetchAll();
} catch (PDOException $e) {
exit('取得失敗' . $e->getMessage());
}
return $categories;
}
}
```
- index.phpに新しいroutingを追加する
/index.php
```php=
<?php
ini_set('display_errors', "On");
require_once ('./route/RequestUrl.php');
require_once ('./controller/PostController.php');
require_once ('./controller/UserController.php');
require_once ('./controller/CategoryController.php');
$request = new RequestUrl();
$url = $request->getPathInfo();
// routing
switch ($url){
case '/':
case '/post':
$postController = new PostController();
$postController->indexAction();
break;
case '/user/add':
$userController = new UserController();
$userController->addAction();
break;
case '/login':
$userController = new UserController();
$userController->loginAction();
break;
case '/logout':
$userController = new UserController();
$userController->logoutAction();
break;
case '/category/add':
$categoryController = new CategoryController();
$categoryController->addAction();
break;
}
```
- 下記のURLにアクセスする
http://localhost:8888/php_tutorial/category/add
![](https://i.imgur.com/ioStpb6.png)
### 3.2.4 記事登録機能を作る
- PostControllerに記事投稿の処理を追記する
/controller/PostController.php
```php=
<?php
ini_set('display_errors', "On");
require_once ('Controller.php');
require_once ('./model/PostModel.php');
require_once('./model/PostModel.php');
require_once('./model/CategoryModel.php');
/**
* Class PostController
*/
class PostController extends Controller
{
/**
* PostController constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* 記事一覧
*/
public function indexAction()
{
$postModel = new PostModel();
$posts = $postModel->get();
require("./public/view/post_index.php");
}
/**
* 記事投稿
*/
public function addAction(){
$categoryModel = new CategoryModel();
$categories = $categoryModel->getAll();
$errors = [];
$title = '';
$content = '';
if (@$_POST['submit']) {
$title = $_POST['title'];
$content = $_POST['content'];
$categoryId = $_POST['category'];
// バリデーションチェック
$errors = $this->addValidation($title, $content, $categoryId);
// バリデーションエラーがない場合
if (count($errors) === 0) {
// DBに登録する
$postModel = new PostModel();
$postModel->add($title, $content, $categoryId);
header('Location: ../');
exit();
}
}
require("./public/view/post_add.php");
}
/**
* @param $title
* @param $content
* @param $categoryId
* @return array
*/
private function addValidation($title, $content, $categoryId){
$errors = [];
if (empty($title)){
$errors['title'] = 'タイトルがありません。<br>';
}
if (mb_strlen($title) > 80){
$errors['title'] = 'タイトルが長すぎます。<br>';
}
if (empty($content)){
$errors['content'] = '本文がありません。<br>';
}
return $errors;
}
}
```
- view配下にpost_add.phpを配置する
/public/view/post_add.php
```htmlmixed=
<?php
ini_set('display_errors', "On");
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Clean Blog - Start Bootstrap Theme</title>
<!-- Bootstrap core CSS -->
<link href="../public/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom fonts for this template -->
<link href="../public/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet'
type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800'
rel='stylesheet' type='text/css'>
<!-- Custom styles for this template -->
<link href="../public/css/clean-blog.min.css" rel="stylesheet">
<style>
.error-message {
color: red;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container">
<a class="navbar-brand" href="./">Re:Build</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
aria-label="Toggle navigation">
Menu
<i class="fa fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="../">Home</a>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px; margin-right: 20px;" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
ブログ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="../post/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<div class="btn-group">
<span style="color: white; font-size: 13px;" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
カテゴリ管理
</span>
<div class="dropdown-menu">
<a class="dropdown-item" href="../category/add">登録</a>
<a class="dropdown-item" href="#">一覧</a>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="../logout">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Page Header -->
<header class="masthead" style="background-image: url('../public/img/home-bg.jpg')">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="site-heading">
<h1>Tech Blog</h1>
<span class="subheading">challenge</span>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<form method="post" action="./add">
<div class="post">
<?php if (count($errors) > 0) { ?>
<?php foreach ($errors as $error) { ?>
<p class="error-message"><?php echo $error ?></p>
<?php } ?>
<?php } ?>
<h2>記事投稿</h2>
<p>題名</p>
<p><input type="text" name="title" size="40" class="form-control" value="<?php echo $title ?>"></p>
<p>本文</p>
<p><textarea name="content" class="form-control" rows="8"
cols="40"><?php echo $content ?></textarea></p>
<p>カテゴリ</p>
<select name="category" class="form-control">
<?php
foreach ($categories as $category) {
echo '<option value="', $category['id'], '">', $category['name'], '</option>';
}
?>
</select>
<p><input class="btn btn-primary" name="submit" type="submit" value="投稿"></p>
</div>
</form>
</div>
</div>
</div>
<hr>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<ul class="list-inline text-center">
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-facebook fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">Copyright © Your Website 2018</p>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="../public/vendor/jquery/jquery.min.js"></script>
<script src="../public/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Custom scripts for this template -->
<script src="../public/js/clean-blog.min.js"></script>
</body>
</html>
```
- Model配下のPostModelに記事投稿の処理を追記する
/model/PostModel.php
```php=
<?php
require_once('Model.php');
/**
* Class PostModel
*/
class PostModel extends Model
{
/**
* @var PDO
*/
private $pdo;
/**
* PostModel constructor.
*/
function __construct()
{
parent::__construct();
$this->pdo = parent::getPdo();
}
/**
* @return array
*/
public function get()
{
$st = $this->pdo->query("SELECT
posts.id,
posts.title,
posts.content,
posts.category_id,
posts.date,
categories.name as category_name
FROM posts INNER JOIN categories
ON posts.category_id = categories.id;");
try {
$posts = $st->fetchAll();
} catch (PDOException $e) {
exit('取得失敗' . $e->getMessage());
}
return $posts;
}
/**
* @param $title
* @param $content
* @param $categoryId
*/
public function add($title, $content, $categoryId)
{
$stmt = $this->pdo->prepare("INSERT INTO posts (title, content, category_id) VALUES (:title, :content, :category_id)");
$stmt->bindParam(':title', $title, PDO::PARAM_STR);
$stmt->bindValue(':content', $content, PDO::PARAM_STR);
$stmt->bindValue(':category_id', $categoryId, PDO::PARAM_INT);
try {
$stmt->execute();
} catch (PDOException $e) {
exit('登録失敗' . $e->getMessage());
}
}
}
```
- index.phpに新しいroutingを追加する
/index.php
```php=
<?php
ini_set('display_errors', "On");
require_once ('./route/RequestUrl.php');
require_once ('./controller/PostController.php');
require_once ('./controller/UserController.php');
$request = new RequestUrl();
$url = $request->getPathInfo();
// routing
switch ($url){
case '/':
case '/post':
$postController = new PostController();
$postController->indexAction();
break;
case '/post/add':
$postController = new PostController();
$postController->addAction();
break;
case '/user/add':
$userController = new UserController();
$userController->addAction();
break;
case '/login':
$userController = new UserController();
$userController->loginAction();
break;
case '/logout':
$userController = new UserController();
$userController->logoutAction();
break;
}
```
- 下記のURLにアクセスする
http://localhost:8888/php_tutorial/post/add
![](https://i.imgur.com/u1TIOrC.png)
# 参考資料
- https://speakerdeck.com/bumptakayuki/phpru-men
- https://speakerdeck.com/bumptakayuki/phpwei-jing-yan-zhe-woyu-terudu-zi-huremuwakufalsezuo-rifang
# 参考リポジトリ
https://github.com/bumptakayuki/blog_intern
# 今後の課題
## TODO管理アプリを作成する
### 機能一覧
- ユーザ登録
- ログイン
- ログアウト
- タスク登録
- タスク編集
- タスク削除
- タスク一覧
- タスクはカテゴリでフィルタリングできること
- カテゴリ登録
- カテゴリ編集
- カテゴリ削除
- カテゴリ一覧
### やる事
- データベース設計
- 画面遷移図作成
- 開発
### 使用する技術
- HTML
- CSS(bootstrapも可)
- JavaScript
- PHP