Практика 1: Авторизация

Задача:

1) Создать БД (вручную с помощью запроса) с именем ‘task1’. Создать таблицу ‘user’ (id — уникальный номер, name — имя, login — логин, password — пароль, enter — кол-во входов, remove — флаг удаления)

2) Создать верстку согласно макету (см. ниже) — это страница с формами входа (логин, пароль) и регистрации (логин, пароль, имя), 2 таблицы со всеми пользователями (не выводить столбец ид) и с удаленными.

3) Авторизацию и регистрацию выполнить 2 способами. Стандартным (form action) и ajax без перезагрузки страницы. При входе увеличивать счетчик входов на 1 и записывать в БД поле ‘enter’. Если пользователь авторизован выводить таблицы, если вышел формы авторизации.

Решение:

Шаг 1:

Для начала создадим новую БД с именем ‘task1’, и подключится к ней. Для этого вызовем команду в консоле БД.

create database task1;
use task1;

Создадим таблицу ‘user’.

CREATE TABLE user (
    `id` INT NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) DEFAULT ' ',
    `login` VARCHAR(50) NOT NULL,
    `password` VARCHAR(50) DEFAULT 0,
    `enter` INT DEFAULT 0,
    `remove` INT DEFAULT 0,
    PRIMARY KEY (`id`)
);

Шаг 2:

По предложенному макету нам нужно сверстать страничку. Сначала разметим страницу на блоки, а потом оформим блоки. Итак нам нужно разбить монитор на 2 части. Укажем у блоков class чтобы потом привязать к ним нужные стили.

<div class="col">
	Первая часть с полями входа и регистрации
</div>
<div class="col">
	Вторая часть вывод таблиц и приветствия
</div>

В первой части у нас 2 формы. У формы укажем ссылку на обработчик в поле action. Туда будут отправлены данные из формы. А также укажеи идентификатор id по которому сможем обратится из js.

<form id="enter">
   ...
</form>
<form action="index.php" id="add">
   ...
</form>

Создадим заголовок с классом title, чтобы потом указать нужные стили. 

<div class="title">Авторизация</div>

Также нужно создать поле вывода ошибок, например если пользователь ввел неправильный пароль, нужно сообщить ему об этом. С классом message.

<div class="message">Ошибка</div>

И саму форму с 2/3 полями ввода. Для поля имени и логина подойдет открытое поле типа text, а для пароля нужно закрытое (чтобы никто не подсмотрел за спиной) с типом password. Заполним поля name — именем переменной в которую будет предаваться введенное значение.

<input type="text" placeholder="Логин" name="login">
<input type="password" placeholder="Пароль" name="password">

Как видите есть еще поле placeholder оно служит для отображения подсказки в незаполненное поле. Нам осталось добавить кнопку, по которой форма будет отправлена для дальнейшей обработки.

<button>Войти</button>

Регистрацию сделаем классическим способом:

<input type="submit" name="registration" value="Зарегистрироваться">

И добавим разделитель <hr> для красоты. Вот как выглядит весь кусок с формами.

<div class="col">
    <div class="title">Авторизация</div>
    <div class="message">Ошибка</div>
    <form id="enter">
        <input type="text" placeholder="Логин" name="login">
        <input type="password" placeholder="Пароль" name="password">
        <button>Войти</button>
    </form>
    
    <hr>
    
    <div class="title">Регистрация</div>
    <div class="message">Ошибка</div>
    <form action="index.php" id="add" method="post">
        <input type="text" placeholder="Имя" name="name">
        <input type="text" placeholder="Логин" name="login">
        <input type="password" placeholder="Пароль" name="password">
        <input type="submit" name="registration" value="Зарегистрироваться">
    </form>
</div>

Во второй части у нас есть шапка с логином и кнопкой выход. Создадим соответствующий блок с классом login в нем выведем имя пользователя с классом name, разделитель с классом line и кнопку выйти.

<div class="login">
    <div class="name">ivan</div>
    <div class="line"></div>
    <a>выйти</a>
</div>

Далее у нас располагаются 2 таблицы. Одна с активными пользователями, другая с удаленными. Они будут одинаковые. Для заголовка используем уже готовый класс title. Строкой таблицы обозначается tr, ячейкой колонки td. Таблица состоит также из шапки thead и основной части tbody. В конце таблицы создадим кнопку-ссылку для удаления элемента. Создадим для примера 3 пользователя, в дальнейшем таблица будет заполняться из БД с помощью цикла. Вот как выглядит разметка для таблицы «Активные».

<div class="title">Активные</div>
<table>
    <thead>
    <tr>
        <td>Имя</td>
        <td>Логин</td>
        <td>Пароль</td>
        <td>Кол-во входов</td>
        <td></td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Вася</td>
        <td>Vasya</td>
        <td>12345</td>
        <td>0</td>
        <td><a href="#">х</a></td>
    </tr>
    <tr>
        <td>Петр</td>
        <td>petr</td>
        <td>gahjkm</td>
        <td>2</td>
        <td><a href="#">х</a></td>
    </tr>
    <tr>
        <td>Иван</td>
        <td>ivan</td>
        <td>11111</td>
        <td>11</td>
        <td><a href="#">х</a></td>
    </tr>
    </tbody>
</table>

Вся разметка готова. Теперь нужно прописать классы. Формат такой, сначало все инструкции, потом описание.

body{
    width: 700px;
    margin: 0 auto;
    font-family: 'Roboto', sans-serif;
}

Обращаемся по тегу

width: установим ширину контента
margin: сейчас блок приклеен к левой стороне экрана, теперь нужно выровнять все по центру, первое значение это по y (т.е. сверху и снизу) второе значение по х (т.е. слева и справа). auto — автоматически посчитает отступы и выровняет.
font-family: просто хочется другой шрифт 

a{
    text-decoration: none;
}

text-decoration: убираем у всех ссылок нижнее подчеркивание.

.col {
    width: 50%;
    float: left;
    padding: 38px;
    box-sizing: border-box;
}

Обращаемся по классу
width: делим пополам, получается 2 колонки
float: блоки отображаются друг под другом, а нам надо рядом, для этого установим обтекание. Блоки станут как вода течь влево, стараясь втиснутся в область, если не получится то перенесутся вниз. в этом случае 2 блока встанут рядом.
padding: Сделаем отступы для красоты, чтобы блоки не были склеенными. Можно установить 1 значение, тогда отступы будут со всех 4х сторон, с каждой стороны по 38px.
box-sizing: Отступы начинают «толкаться» и блоки съезжают вниз, потому что ширина блока 50% + отступы с 2х сторон по 38px и таких блоков 2, получается (50% + (38px*2) * 2) = 100% + 152px уже больше 100%. Поэтому второй блок съедет вниз. С помощью box-sizing можно сообщить браузеру, что отступы нужно вписать в ширину. Т.о. 50% поглотят отступы. Другими словами, отступы отталкивались наружу, а теперь внутрь.

.title {
    color: #252525;
    text-align: center;
    font: 500 26px roboto;
    margin-bottom: 20px;
}

color: цвет текста
text-align: выравнивание текста по центру блока в котором он находится
font: еще один способ, первое число — жирность текста, второе — размер, третье имя шрифта.
margin-bottom: отступ снизу

.message {
    display: none;
    margin-bottom: 11px;
    color: red;
    font-size: 13px;
}

display: скроем (none), т.к. вначале ошибка не отображается, при выводе ошибки заменим на block
color: цвет текста, есть текстовый набор red соответствует #ff0000 или #f00 или rgb(255,0,0)
font-size: размер шрифта

input {
    width: 100%;
    border: 1px solid #e4e5e4;
    padding: 10px 13px;
    margin-bottom: 11px;
    box-sizing: border-box;
}

width: если не установить будет по ширине контента, т.о. будет короче родительского блока.
border: красивую рамку первый параметр — ширина, второй — стиль рамки (может быть точками или тире, у нас линия), третий — цвет

button {
    width: 100%;
    height: 42px;
    background: red;
    color: #fff;
    border: none;
    padding: 0;
    margin: 0;
}

height: высоту можно и на padding, как альтернатива
background: установили цвет фона, по тем же правилам что и color
text-transform: сделали все буквы заглавными (перевели в верхний регистр)
border: сбросили рамку
margin и padding: обнулили

hr{
    margin: 20px 0;
    border: none;
    border-top: 1px solid #e4e5e4;
}

border: сбросили всю рамку
border-top: перезаписали только верхнюю границу

.login {
    background: #f4f4f4;
    padding: 12px;
    border: 1px solid #e4e5e4;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
}

display: flex — очень удобны если нужно организовать выравнивание группы блоков по высоте или между собой по центру.
justify-content: выровняли блоки в центре родительского
align-items: выровняли содержимое блоков в центре

.login div,
.login a{
    color: #5a5a5a;
    font-size: 13px;
}
.login a:hover{
    color: red;
}
.line {
    width: 1px;
    height: 11px;
    background: #5a5a5a;
    margin: 0 11px;
}

a:hover — класс включится при наведении на элемент, в нашем случаем поменяем цвет текста ссылки.

table {
    margin-bottom: 20px;
    width: 100%;
    border: 1px solid #e4e5e4;
    border-collapse: collapse;
}
table td {
    color: #5a5a5a;
    font-size: 13px;
    padding: 4px 8px;
}
table thead tr td {
    color: #a0a0a0;
    font-size: 11px;
    border-bottom: 1px solid #e4e5e4;
}
table tbody tr:nth-child(even) {
    background: #f4f4f4;
}

border-collapse — скрыли отступы в таблице между ячейками

Все, стили готовы. Осталось собрать все вместе, подключить гуг шрифты.

<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">

Написать заголовок:

<title> Вход на сайт </title>

Написать заголовок и установить кодировку:

<title> Вход на сайт </title>
<meta charset="UTF-8">

Ну вот и все, вот полный файл с готовой версткой:

Шаг 3:

Красота есть, теперь наше «пугало» хочет мозги. Переименуем верстку из .html в index.php. При клике по кнопке «Зарегистрироваться» данные форма передаст сама себе. Т.е. на адрес данного скрипта, его можно указать и полным путем сайта http://localhost/index.php. Обратите внимание данные передаются методом post (атрибут method=»post») т.к. в форме есть пароль. Если мы передадим данные в get, то они появятся в адресной панели браузера и его могут увидеть посторонние. Добавим в начало файла обработку:

<?
print_r($_REQUEST);
?>

Теперь при заполнении полей мы получим результат в самом вверху экрана:

Array (  [name] => [login] => [password] => [registration] => Зарегистрироваться)

Мы увидим что данные пришли, теперь их можно проверить и сохранить в базу. Проверим поля на пустоту, а пароль на длину, ошибки сохраним в массив $errors. Закомментируем предыдущую проверку чтобы не мешала. Наличие параметра registration говорит нам о том что кнопка была нажата. Сделаем проверку, чтобы ошибки не выводились преждевременно.

<?
//print_r($_REQUEST);

$errors = [];
if ($_REQUEST['registration']) {
    if (empty ($_REQUEST['name'])) $errors[] = "Не заполнено поле ИМЯ";
    if (empty ($_REQUEST['login'])) $errors[] = "Не заполнено поле ЛОГИН";
    if (mb_strlen ($_REQUEST['password'])<=3) $errors[] = "Поле ПАРОЛЬ должно содержать более 3 символов";
}

print_r($errors);
?>

empty() — возвращает true если переменная пустая

mb_strlen() — подсчет кол-ва символов в строке. Функция strlen() — ее предок (без mb_ впереди), корректно работает с кодировкой utf, русские символы будут считаться за 2. В старых кнопочных телефонах раньше так было в смс, латинские буквы считаются за 1, а кириллица за 2. 

Теперь нужно вывести ошибки (если они есть!) в нужное место в верстке, добавим проверку и поправим стиль:

<div class="message" <?if(count ($errors)):?> style="display:block;" <?endif;?> >
    <?foreach ($errors as $error):?>
        <p><?=$error?></p> или так <? echo $error ?>
    <?endforeach;?>
</div>

count () — подсчитаем кол-во элементов в массиве

Как вы видите php код мы можем вставлять в любое место. В первой строке мы хотим изменить стиль display:none (мы его прописали в стилях) — на display:block. Если массив пустой, то условие не выполнится. Далее перебираем циклом foreach массив с ошибками, сохраняя каждую ошибку во временную переменную $error. И тут же выводим ее, конструкция <?= … ?> аналогична конструкции <? echo … ?>. Чтобы соблюдались отступы завернем вывод в тег <p>.

Хорошо бы подставить ранее введенные данные, чтобы не вводить их повторно. Подставим полученные из глобального массива данные в параметр value.

<input type="text" placeholder="Имя" name="name" value="<?=$_REQUEST['name']?>">
<input type="text" placeholder="Логин" name="login" value="<?=$_REQUEST['login']?>">
<input type="password" placeholder="Пароль" name="password" value="<?=$_REQUEST['password']?>">

Атрибут value хранит текущее значение инпута. Туда и выводим наши введенные ранее уже знакомым способом.

Теперь нам нужно добавить запись в БД. Для этого нужно создать подключение, проверить существует ли в базе пользователь с таким логином, и добавить запись.

Создаем подключение в самом начале файла и проверяем. Подключение является объектом. В дальнейшем мы будем пользоваться методами (функциями) данного объекта для создания запросов и т.п.

$mysqli = new mysqli("localhost", "root", "", "task1");
print_r($mysqli);

Результат объект с данными:

mysqli Object ( [affected_rows] => 0 [client_info] => ...

Первый параметр путь к серверу, т.к. сервер на локальной машине то localhost, далее логин и пароль. И последним параметром имя нашей базы, мы создали task1.

Сделаем проверку на существование записи в БД.

$login = $mysqli->real_escape_string($_REQUEST['login']);
$result = $mysqli->query("SELECT * FROM `user` WHERE `login`='{$login}'");
print_r($result);

Функция $mysqli->real_escape_string() — экранирует кавычки, для безопасности. Подробнее изучите тему «SQL инъекции».

Функция $mysqli->query() делает запрос в БД. Как видите мы пользуемся объектом, который создали ранее. Сам запрос звучит так выбрать (SELECT) все поля (*) из таблицы (FROM) имя таблицы (user) где (WHERE) имя поля login (login) равно значению (='{$login}’).

Обратите внимание на скобки и кавычки. В запросе значение должно стоять в одинарных кавычках. С помощью фигурных скобок { } мы вставляем переменную $login в строку. Имя же таблицы и имена переменных можно взять в кавычки-апострофы, но это не обязательно.

Мы сохраняем возвращаемые значения в переменную $result. Теперь мы можем дернуть метод num_rows, он возвращает кол-во затронутых строк при запросе, если там 0 значит запись не найдена.

Теперь если у нас нет ошибок и записей в таблице добавим запись.

if (!count ($errors) && !$result->num_rows) {

    $name = $mysqli->real_escape_string($_REQUEST['name']);
    $login = $mysqli->real_escape_string($_REQUEST['login']);
    $password = $mysqli->real_escape_string($_REQUEST['password']);
    
    $mysqli->query("
        INSERT INTO user SET
            `name` = '{$name}',
            `login` = '{$login}',
            `password` = '{$password}'
    ");

}

Условие читать так: если НЕ кол-во ошибок и НЕ кол-во строк, то выполнить код. С помощю НЕ мы инвертируем вывод, count ($errors) вернет true если есть ошибки, а нам надо true если ошибок нет. Точно так же и во втором случае. Далее мы экранируем уже знакомой нам функцией данные и запихиваем в таблицу. Добавим сообщение о том что пользователь существует (выделено жирным). Вот как будет выглядеть весь кусок кода регистрации.

$errors = [];
if ($_REQUEST['registration']) {
    if (empty ($_REQUEST['name'])) $errors[] = "Не заполнено поле ИМЯ";
    if (empty ($_REQUEST['login'])) $errors[] = "Не заполнено поле ЛОГИН";
    if (mb_strlen ($_REQUEST['password'])<=3) $errors[] = "Поле ПАРОЛЬ должно содержать более 3 символов";

    $login = $mysqli->real_escape_string($_REQUEST['login']);
    $result = $mysqli->query("SELECT * FROM `user` WHERE `login`='{$login}'");

    if (!count ($errors) && !$result->num_rows) {
        $name = $mysqli->real_escape_string($_REQUEST['name']);
        $login = $mysqli->real_escape_string($_REQUEST['login']);
        $password = $mysqli->real_escape_string($_REQUEST['password']);
        
        $mysqli->query("
            INSERT INTO `user` SET
                `name` = '{$name}',
                `login` = '{$login}',
                `password` = '{$password}'
        ");
    } else if ($result->num_rows) {
        $errors[] = "Пользователь с таким ЛОГИНОМ уже существует";
    }
}

Как видите все действия выполняются если существует переменная ‘registration’ это сделано для того, чтобы программа не выполняла в пустую действия и не нагружала сайт лишний раз.

Можно сделать вывод зарегистрированных пользователей. Выберем всех не удаленных пользователей и сохраним выборку в переменную:

$users = $mysqli->query("SELECT * FROM `user` WHERE `remove`='0'");

Теперь переберем все значения и выведем в таблицу. Удалим из верстки все <tr> кроме одного и поместим его в цикл:

<?while($user = $users->fetch_assoc()):?>
<tr>
    <td><?=$user['name']?></td>
    <td><?=$user['login']?></td>
    <td><?=$user['password']?></td>
    <td><?=$user['enter']?></td>
    <td><a href="?remove=<?=$user['id']?>">х</a></td>
</tr>
<?endwhile;?>

Цикл while будет перебирать объект $users. Каждый раз дергая метод fetch_assoc(). Этот метод возвращает данные текущей записи в цикле в виде массива. Мы сохраняем этот массив в переменную $user. Далее выводим данные обращаясь к ним по имени колонки БД. Обратите внимание мы сохранили значение id в html атрибут href. Так мы будем удалять запись.

То же самое повторяем и для удаленных.

$removes = $mysqli->query("SELECT * FROM `user` WHERE `remove`='1'");

Ниже:

<?while($remove = $removes->fetch_assoc()):?>
<tr>
    <td><?=$remove['name']?></td>
    <td><?=$remove['login']?></td>
    <td><?=$remove['password']?></td>
    <td><?=$remove['enter']?></td>
</tr>
<?endwhile;?>

Если мы нажмем на «крестик» удаления, то перейдем по ссылке на страницу и у нас появится параметр $_REQUEST[‘remove’] который будет содержать ИД удаляемой записи. Пишем в верху скрипта код удаления:

if ($_REQUEST['remove']) {
    $id = (int)$_REQUEST['remove'];
    $mysqli->query("UPDATE `user` SET `remove`='1' WHERE `id`='{$id}'");
}

Если есть переменная ‘remove’ то пытаемся «удалить». Приводим значение к числу, в этом случае экранировать значение не нужно. И обновляем запись, изменяя флаг удаления на 1. Проверять существует ли запись не нужно, если записи такой нет. то ничего не произойдет.

Итак, последний шаг это авторизация и сокрытие/открытие областей. Авторизацию мы сделаем с помощью технологии ajax. Нам понадобится библиотека jquery возьмем ее с cnd серверов гугла. В head добавим строку.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

Отладку будем производить с помощью консоли. Нажав на f12 в браузере откроется панель разработчика. На вкладке network смотрим какие запросы уходят. На вкладке console смотрим ошибки js. Добавим в конец файла скрипт отправки данных на сервер. Вначале создадим обработчик события нажатие на кнопку «Войти».

$('#enter button').on('click', function(event){
    ...
});

Находим нужную кнопку $(‘#enter button’), слушаем событие клик .on(‘click’ и выполняем функцию если событие произошло function(event){.

Далее отменим стандартное событие нажатие кнопки (чтобы форма не перезагрузилась):

event.preventDefault();

Заблокируем кнопку чтобы пользователь несколько раз не отправлял данные. Обратите внимание внутри функции нам доступна переменная this, она ссылается на себя, т.е. кнопку button:

$(this).attr("disabled", true);

Воспользуемся еще одной функцией библиотеки jquery. Первый параметр — адрес скрипта, у нас вызываем сам себя. Объект данных, получаем значения инпутов через селектор по ид и тег+атрибут, с помощью функции val() получаем данные. Далее функция которая выполнится при получении ответа, в скобках имя переменной, в которую запишутся ответные данные. Внутри функции включим кнопку «Войти». Важно что из функции мы не можем обратиться по this к кнопке, т.к. this теперь сам метод .post(). Т.о. кнопка войти по клику блокируется, пока не придет ответ от сервера. И наконец формат данных, у нас это json.

$.post(
    'index.php',
    {
        action: 'enter',
        login: $('#enter input[name="login"]').val(),
        password: $('#enter input[name="password"]').val(),
    },
    function(data)
    {
        ...
        $('#enter button').attr("disabled", false);
    }, "json");

Теперь посмотрим php. Вначале скрипта выведем данные которые приходят.

print_r($_REQUEST);

Ответ, как и то что мы отправляем, можно посмотреть в консоли. Выбрав в списке network нужный запрос и посмотрев вкладки headers — отправленные данные, response — ответ. Там мы увидим массив с данными, значит скрипт работает.

Array
(
    [action] => enter
    [login] => Vasya
    [password] => 12345
...

Обработаем вход. Для начала запустим сессии, т.к. именно по ним мы будем определять авторизованного пользователя. Добавим в начало файла:

session_start();

Теперь обработаем входные данные так же как при регистрации. Алгоритм такой, проверим по 2м данным есть ли запись с таким логином и паролем, если нет выводим ошибку, если есть запишем в сессию. Вначале добавим массив $answer — он будет содержать данные ответа. Определим переменную которая будет отвечать успешно ли мы авторизовались result булево true или false (по умолчанию). Далее получаем выборку по логину и паролю, знакомым нам способом и сразу пытаемся получить массив с данными ->fetch_assoc(). Если массив НЕ пустой, то записываем в массив сессии $_SESSION нужные нам данные, это имя и ид, а так же не забыли увеличить счетчик входов на 1. Если пустой, то добавляем в массив ответа ошибку. В Конце преобразуем массив ответа в формат json и выводим. Обязательно завершаем выполнение скрипта exit(), в этом случае работа скрипта остановится.

if ($_REQUEST['action']=='enter') {
    $answer['result'] = true;
    
    $login = $mysqli->real_escape_string($_REQUEST['login']);
    $password = $mysqli->real_escape_string($_REQUEST['password']);
        
    $enter = $mysqli->query("SELECT * FROM `user` WHERE `login`='{$login}' AND `password`='{$password}'")->fetch_assoc();
    if ($enter){
        $_SESSION['id'] = $enter['id'];
        $_SESSION['name'] = $enter['name'];
        $mysqli->query("UPDATE `user` SET `enter`=`enter`+1 WHERE `id`='{$enter['id']}'");
    } else {
        $answer['error'] = "Неверно введен ЛОГИН или ПАРОЛЬ";
        $answer['result'] = false;
    }
    
    print json_encode($answer);
    exit();
}

Допишем обработку ответа на js. Если успешно, то перезагружаем (обновляем) страницу, если ошибка, то выводим ее в нужном месте. С помощью функции prev() получаем предыдущий элемент DOM делаем его видимым show() (как вы помните блок с ошибкой скрыт display: none) и записываем в него текст html(). Как видите обращаемся к данным json через переменную которую определили в функции и точку, т.к. это объект data.result.

if (data.result)
    window.location = 'index.php';
else
    $("#enter").prev('.message').show().html(data.error);

Осталось определить видимости и написать выход.

<?if(!$_SESSION['id']):?>
    ... блок авторизации
<?else:?>
    ... блок таблиц
<?endif;?>

Доделаем отображение пользователя и кнопку выхода. Будем передавать пустую переменную quit для выхода. И выведем имя, ранее сохраненное в сессиях.

<div class="login">
    <div class="name"><?=$_SESSION['name']?></div>
    <div class="line"></div>
    <a href="?quit">выйти</a>
</div>

При клике на выход удалим сессию. Функция isset() проверяет существует ли переменная.

if (isset ($_REQUEST['quit'])) {
    $_SESSION = array();
}

Вот в целом и все. Принимаем данные, обрабатываем (тут вся магия), отдаем результат в нужном виде, то ли это html код, или json данные. Таким образом построена вся разработка. Можно улучшить код, придумать разные права пользователям, например на просмотр и удаление (сделать полями 1 и 0). В целом по данной статье вы уже способны написать что угодно, самые важные моменты это:

  • Обрабатывать данные форм и приходящие из ajax
  • Работать с БД
  • Обмениваться данными по ajax

Я бы даже сказал что вам больше ничего и не нужно, в этой статье почти все направления для самообучения. Обучайтесь по всем направлениям параллельно по мере необходимости. Придумывайте мини проекты и отвечайте на возникающие вопросы с помощью поисковика.

Задание:

  • Поменять цвет кнопок на зеленый, поиграть со стилями.
  • Не отображать таблицы если для вывода ничего нет, например нету удаленных.
  • Добавить поля фамилия, возраст.
  • Восстановить из удаленных.
  • Окончательное удаление из БД из удаленных.