Серверное программирование

Веб-дизайн и разработка

В этом уроке мы познакомимся с серверным программированием на языке PHP. Посмотрим, как подключаться к базе данных, выбирать данные из базы и создавать их. Начнем!
Глоссарий
Для успешного освоения материала рекомендуем вам изучить следующие понятия:
PDO
Расширение для PHP, предоставляющее разработчику универсальный интерфейс для доступа к различным базам данных. PDO предлагает единые методы для работы с базами данных, хотя текст запросов может немного отличаться
Массив
Структура данных, хранящая набор значений, идентифицируемых по индексу или набору индексов, принимающих целые значения из некоторого заданного непрерывного диапазона
Ассоциативный массив
Абстрактный тип данных, позволяющий хранить пары вида «ключ-значение» и поддерживающий операции добавления пары, а также поиска и удаления пары по ключу
Видеолекция
Конспект

Подключение к базе данных
Первым делом нужно из главной HTML-страницы сделать PHP-страницу.

1. Переименуйте index. html в index.php.

2. Чтобы в каждом файле не писать подключение к базе, создадим отдельный файл db. php в корне проекта.

3. В нем создаем PDO-подключение и записываем его в переменную pdo.
<?php

$pdo = new PDO('mysql:host=localhost;dbname=personal_website;charset=utf8', 'root', 'root', [
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
4. В подключении указываем хост, имя базы, кодировку, логин, пароль и последним параметром передаем массив настроек, в котором указываем метод выборки в виде ассоциативного массива.
Вывод работ в портфолио
1. Теперь на главной странице подключаем этот файл и пишем запрос на выборку всех записей из таблицы Works.
<?php

require_once "db.php";

$stmt = $pdo->query("select * from works");
$works = $stmt->fetchAll();

?>

<!DOCTYPE html>
...
Важно
Запрос на выборку пишется на языке SQL, поэтому его нужно знать, чтобы работать с базой данных
2. Оставляем только одну картинку в верстке, а остальные удаляем.
...
<div id="lightgallery" class="gallery">
    <a class="img-wrapper" href="assets/img/placeholder.jpg">
        <img src="assets/img/placeholder.jpg" />
    </a>
</div>
...
3. Ее помещаем в цикл foreach.
Важно
Цикл будет перебирать массив works, и в цикле мы будем выводить путь к картинке
4. Плагин, которым мы пользуемся, поддерживает вывод названия картинок, для этого название нужно написать в атрибуте data-sub-html.
<div id="lightgallery" class="gallery">
    <?php foreach($works as $work): ?>
    <a class="img-wrapper" 
 data-sub-html="<?= $work['name'] ?>" 
 href="<?= $work['file_path'] ?>">
        <img src="<?= $work['file_path'] ?>" />
    </a>
    <?php endforeach; ?>
</div>
Все картинки, которые выводятся на странице, взяты из базы. Если вы нажмете на картинку, то внизу будет название, взятое также из базы.
Отправка формы на сохранение
1. Указываем action для формы.

Для тега формы нужно указать action — это страница, на которую будет отправлена форма, и метод, которым будет отправлена форма.
<form action="feedback.php" method="POST">
    ...
</form>
2. Задаем имена текстовым полям.

Всем полям необходимо указать имена и добавить атрибут required, чтобы они проверялись на заполненность.
<form action="feedback.php" method="POST">
   <input name="name" required type="text" placeholder="Как к вам обращаться">
   <input name="email" required type="email" placeholder="Ваш email">
   <textarea name="text" required rows="4" placeholder="Сообщение"></textarea>
   <input class="btn btn-bg" type="submit" value="Отправить">
</form>
Теперь, заполнив и отправив форму, мы попадем на страницу feedback. php, которую нам нужно создать.

3. Создаем страницу feedback.php.

В начале файла подключаем файл с подключением к базе данных.
<?php
require_once "db.php";
4. Валидируем поля.

Далее необходимо проверить, что поля, которые приходят POST-запросом, не пустые.
if( !empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['text']) )
{
   ...   
}
5. Подготавливаем запрос.

Если они не пустые, то подготавливаем наш запрос.
if( !empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['text']) )
{
   $stmt = $pdo->prepare("insert into messages(name, email, text) values(?,?,?)");   
}
6. Выполняем запрос.

Вместо значений указываем вопросительные знаки, чтобы исключить инъекции, и затем выполняем этот запрос, передавая туда наши данные из запроса.
if( !empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['text']) )
{
   $stmt = $pdo->prepare("insert into messages(name, email, text) values(?,?,?)");   
   $stmt->execute([
       $_POST['name'],
       $_POST['email'],
       $_POST['text']
   ]);
}
7. Перенаправляем на главную страницу.

После условия делаем перенаправление на главную страницу, чтобы в любом случае мы оказались на главной.
...
header("Location: index.php");
Теперь, если отправить форму на главной странице, то в базе данных появится новая запись.
Отправка формы на сохранение
Следующий шаг — нам нужно где-то выводить все сообщения от пользователей. Обычно этот функционал делается в панели администратора, доступ к которой можно получить только после авторизации, но мы не будем так все усложнять — все-таки цель урока не в этом, поэтому создадим папку Admin, а в ней — index.php.

1. Делаем базовую разметку и подключаем стили.
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Админка</title>
    <link rel="stylesheet" href="../assets/css/main.css">
</head>
<body>
</body>
</html>
2. Подключаем базу данных и выбираем все сообщения из таблицы сообщений.
<?php

require_once "../db.php";

$stmt = $pdo->query("select * from messages");
$messages = $stmt->fetchAll();

?>

<!DOCTYPE html>
...
3. Создаем заголовок и делаем таблицу.

В таблице будут столбцы: номер по порядку, имя, почта, текст сообщения, дата и время.
<body>
    <div class="container">
  <h2>Сообщения</h2>

        <table>
            <tr>
                <th>#</th>
                <th>Имя</th>
                <th>Email</th>
                <th>Текст</th>
                <th>Дата и время</th>
            </tr>

            <tr>
                <td>1</td>
                <td>Имя</td>
                <td>Почта</td>
                <td>Сообщение</td>
                <td>Дата и время</td>
            </tr>            
        </table>
    </div>
</body>
4. Выводим все сообщения.

Выводим все сообщения с помощью цикла foreach. Имя, почту и текст мы обернем в функцию htmlspecialchars, чтобы исключить внедрение скриптов на нашу страницу, т.к. мы не знаем, что пользователь мог нам прислать.
<body>
    <div class="container">
  <h2>Сообщения</h2>

        <table>
            <tr>
                <th>#</th>
                <th>Имя</th>
                <th>Email</th>
                <th>Текст</th>
                <th>Дата и время</th>
            </tr>

            <?php foreach ($messages as $key => $message) : ?>
                <tr>
                    <td><?= $key + 1 ?></td>
                    <td><?= htmlspecialchars($message['name']) ?></td>
                    <td><?= htmlspecialchars($message['email']) ?></td>
                    <td><?= htmlspecialchars($message['text']) ?></td>
                    <td><?= $message['created_at'] ?></td>
                </tr>
            <?php endforeach; ?>           
        </table>
    </div>
</body>
5. Проверяем результат.

Переходим на страницу website/admin, видим таблицу со всеми сообщениям из базы данных.
Такая таблица неудобна для просмотра, поэтому улучшим ее внешний вид.

6. Улучшаем таблицу.

Зададим таблице атрибут border.
<table border="1">
  ...
</table>
Напишем немного стилей, чтобы ячейки имели отступ и не прилипали друг к другу.
...
<link rel="stylesheet" href="../assets/css/main.css">
<style>
   table {
      border-collapse: collapse;
      width: 100%;
   }

   td,
   th {
      padding: 10px;
   }
</style>
...
Это всегда помогает сделать таблицу красивее.
Так намного лучше.
Вывод всех изображений
Теперь нужно сделать вывод всех работ.

1. Копируем запрос на выборку данных и меняем название таблицы.
...
$messages = $stmt->fetchAll();

$stmt = $pdo->query("select * from works");
$works = $stmt->fetchAll();

?>
...
2. Копируем портфолио с главной страницы и немного меняем пути.
<h2>Портфолио</h2>

<div id="lightgallery" class="gallery">
    <?php foreach ($works as $work) : ?>
        <a class="img-wrapper" 
 	     data-sub-html="<?= $work['name'] ?>" 
     href="http://website/<?= $work['file_path'] ?>">
             <img src="http://website/<?= $work['file_path'] ?>" />
        </a>        
    <?php endforeach; ?>
</div>

<br><br><br><br>

<h2>Сообщения</h2>
...
Мы указали абсолютные пути к картинкам, потому что эта страница находится во вложенной папке Admin. В результате, на странице администратора выводится список всех работ.
Теперь нужно к каждой картинке добавить ссылку на удаление работы. Для этого немного модифицируем нашу верстку и напишем пару стилей.
<style>
...

.admin-img-wrapper {
    margin-bottom: 30px;
}
.admin-img-wrapper .img-wrapper {
    margin-bottom: 0;
}
</style>

...

<div id="lightgallery" class="gallery">
   <?php foreach ($works as $work) : ?>
<div class="admin-img-wrapper">
          <a class="img-wrapper" 
       data-sub-html="<?= $work['name'] ?>"
       href="http://website/<?= $work['file_path'] ?>">
                 <img src="http://website/<?= $work['file_path'] ?>" />
          </a>
          <a href="remove.php?id=<?= $work['id'] ?>">Удалить</a>
    	</div>
    <?php endforeach; ?>
</div>
4. В ссылке на удаление укажем адрес remove. php и передадим туда ID записи как GET-параметр.
Удаление работы из портфолио
Создадим файл remove.php в папке admin.

1. В начале файла подключаем базу данных.
<?php
require_once "../db.php";
2. Проверим, что ID передан.
Важно
Если ID передан, то найдем эту запись в базе, чтобы знать путь до картинки
if(isset($_GET['id']))
{
    $stmt = $pdo->prepare('select * from works where id = ?');
    $stmt->execute([$_GET['id']]);
    $work = $stmt->fetch();
}
3. Если запись есть, то удаляем ее и файл из папки.
…
        $work = $stmt->fetch();
    
    if($work) {
       $stmt = $pdo->prepare('delete from works where id = ?');
       $stmt->execute([$_GET['id']]);

       unlink( dirname(dirname(__FILE__)).'/'.$work['file_path'] );
    }
    
}
4. Перенаправляем обратно на главную в админке.
…
header('Location: index.php');
Добавление работы в портфолио
Поскольку теперь мы можем удалять изображения, нам нужно реализовать загрузку изображений.

1. На странице index. php в папке Admin добавим ссылку на страницу с загрузкой.
...
<h2>Портфолио</h2>

<a href="add.php">Добавить</a>
...
2. Создаем страницу add. php в папке Admin. Создаем базовую разметку и делаем простую форму, состоящую из 2 полей и кнопки.
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Добавление работы в портфолио</title>
</head>
<body>
    <form action="add.php" method="post" enctype="multipart/form-data">
        <input required name="name" type="text" placeholder="Название">
        <input required name="file" type="file">
        <input type="submit" value="Создать">
    </form>
</body>
</html>
Первое поле будет текстовым, а второе — файлом. Оба поля будут обязательные.
Важно
Не забываем указать имена полям. Форма будет отправляться на эту же страницу методом POST, обязательно указываем enctype со значением multipart/form-data, потому что мы передаем файл
3. В php-коде подключаем базу и проверяем, что name не пустое.
<?php
require_once "../db.php";

if ( !empty($_POST['name']) ) {
  ...
}

?>
<!DOCTYPE html>
...
4. Если это так, создаем следующие переменные:
  • apppath — корень приложения
  • filepath — путь к файлу, который начинается от корня нашего приложения, именного его мы будем записывать в базу. К началу имени файла прибавляем временную метку, чтобы файлы с одинаковыми именами не перезаписывали друг друга
  • uploadfile — путь, по которому будет сохранен файл.
...
{
    $apppath = dirname(dirname(__FILE__));
    $filepath = 'uploads/' . time() . basename($_FILES['file']['name']);
    $uploadfile = $apppath . '/' . $filepath;
}
...
5. Перемещаем временный файл в папку Uploads с помощью функции move_uploaded_file.
...
$uploadfile = $apppath . '/' . $filepath;

move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile);
...
6. Подготавливаем запрос на запись в базу и выполняем его. После этого делаем перенаправление на главную страницу в админке.
...

   $stmt = $pdo->prepare("insert into works(name, file_path) values(?,?)");
   $stmt->execute([
      $_POST['name'],
      $filepath
   ]);

   header("Location: index.php");
}

...
Теперь вы знаете, где и как создавать базу данных, и можете попробовать создать свою собственную, однако учтите, что в нашем случае база была очень простой и без связей. Базы данных — это отдельная большая тема, которую стоит изучить, потому что неправильно спроектированная база данных в будущем может серьезно повлиять на разработку проекта и даже привести к необходимости переделывать весь проект. Чтобы закрепить сегодняшний урок, выполните несколько заданий.
Дополнительные материалы
SQL-инъекция
Один из самых доступных способов взлома сайта. Суть таких инъекций — внедрение в данные (передаваемые через GET, POST-запросы или значения Cookie) произвольного SQL кода
MVC
Схема разделения данных приложения, пользовательского интерфейса и управляющей логики на три отдельных компонента — модель, представление и контроллер — таким образом, что модификация каждого компонента может осуществляться независимо
Интерактивное задание
Тест
Для закрепления полученных знаний пройдите тест
Стартуем!
Какой SQL-запрос нужен для выборки данных из таблицы Works?
Дальше
Проверить
Узнать результат
Какая функция позволяет загрузить файл на сервер?
Дальше
Проверить
Узнать результат
Какой способ мы использовали для подключения к базе данных?
Дальше
Проверить
Узнать результат
Какой атрибут обязательно нужно указать для формы при отправке файла?
Дальше
Проверить
Узнать результат
Какая функция позволяет удалить файл?
Дальше
Проверить
Узнать результат
К сожалению, вы ответили неправильно
Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Неплохо!
Но можно лучше. Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Отлично!
Вы отлично справились. Теперь можете ознакомиться с другими компетенциями
Пройти еще раз