Suzuki Takayuki
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 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 &rarr;</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 &copy; 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 &copy; 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 &copy; 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 &copy; 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

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully