Как сохранить JSON на сервере

Мы уже научи­лись отправ­лять на сер­вер и при­ни­мать обрат­но дан­ные в JSON-формате. Финаль­ный шаг — будем хра­нить все наши дан­ные на сер­ве­ре, что­бы полу­чить доступ к ним с любо­го устрой­ства. Это помо­жет нам делать про­ек­ты неза­ви­си­мы­ми от локаль­но­го хра­ни­ли­ща.

Как это будет работать

Мы возь­мём код из про­шло­го про­ек­та про отправ­ку JSON на сер­вер и доба­вим в него новые воз­мож­но­сти.

Логи­ка будет такая:

  1. Мы научим сер­вер каж­дый раз запо­ми­нать дан­ные, кото­рые отпра­вит ему наша стра­ни­ца. Что­бы убе­дить­ся, что дан­ные в поряд­ке, мы сра­зу же в ответ будем посы­лать резуль­тат — то, как сер­вер это запом­нил.
  2. После это­го мы пора­бо­та­ем с дан­ны­ми от сер­ве­ра в понят­ном для JavaScript виде. Для это­го мы немно­го их поме­ня­ем и отпра­вим обрат­но на сохра­не­ние. Это помо­жет нам понять, как рабо­тать с дан­ны­ми в дру­гих про­ек­тах.

👉 Мы спе­ци­аль­но исполь­зу­ем самые про­стые и не самые луч­шие кон­струк­ции в PHP и JavaScript. Наша цель — собрать быст­рый про­то­тип и пока­зать, как мож­но обме­ни­вать­ся дан­ны­ми с сер­ве­ром. Когда раз­бе­рём­ся с этим, мож­но и код пооп­ти­ми­зи­ро­вать. А пока так.

Сохраняем JSON в файл на сервере

Сей­час рабо­та­ет так — по нажа­тию на кноп­ку «Про­ве­рить JSON» наша стра­ни­ца отправ­ля­ет на сер­вер JSON-строку с дву­мя клю­ча­ми: име­нем и фами­ли­ей. Новая зада­ча сер­ве­ра — сохра­нять себе все полу­чен­ные дан­ные и отправ­лять их обрат­но на стра­ни­цу. Что­бы дан­ные не поте­ря­лись после окон­ча­ния рабо­ты про­грам­мы, будем сохра­нять их на сер­ве­ре в файл json.data. Мы выбра­ли такое назва­ние, но вы може­те взять любое дру­гое, имя фай­ла ни на что не вли­я­ет.

Так как по нажа­тию этой кноп­ки стра­ни­ца обра­ща­ет­ся на сер­ве­ре к фай­лу json.php, то и изме­нять тоже нуж­но имен­но его. Сде­ла­ем так:

  1. Полу­ча­ем дан­ные от стра­ни­цы (это уже сде­ла­но в про­шлом про­ек­те).
  2. Про­ве­ря­ем, есть ли на сер­ве­ре нуж­ный нам файл с дан­ны­ми — json.data.
  3. Если есть — запо­ми­на­ем его содер­жи­мое, а если тако­го фай­ла нет — созда­ём его отдель­ной коман­дой.
  4. Всё, что было в этом фай­ле, пере­во­дим в мас­сив, с кото­рым уме­ет рабо­тать PHP. Таким спо­со­бом у нас каж­дая JSON-запись будет хра­нить­ся в отдель­ной ячей­ке мас­си­ва.
  5. Добав­ля­ем новую запись в этот мас­сив — кла­дём в него то, что при­шло со стра­ни­цы.
  6. Запи­сы­ва­ем это всё обрат­но в файл и тут же чита­ем обрат­но из него — так мы убе­дим­ся, что запись про­шла нор­маль­но и мы можем с этим рабо­тать.
  7. Отправ­ля­ем всё содер­жи­мое фай­ла на стра­ни­цу, что­бы там уви­деть, что сер­вер рабо­та­ет как нуж­но.

Зада­ча это­го алго­рит­ма — сохра­нять дан­ные на сер­ве­ре и отда­вать их обрат­но на стра­ни­цу. На язы­ке PHP то же самое будет выгля­деть так:

<?php
// на какие данные рассчитан этот скрипт
header("Content-Type: application/json");
// 1. Получаем данные от страницы
$data = json_decode(file_get_contents("php://input"));
// 2. Проверяем, есть ли на сервере нужный нам файл с данными — json.data.
// Берём новую переменную и пишем в неё имя файла
$filename = 'data.json';
// 3. Если есть — запоминаем его содержимое, а если такого файла нет — создаём его отдельной командой.
if (file_exists($filename)) {
    // Если файл есть — открываем его и читаем данные
    $file = file_get_contents('data.json');
    // Если такого файла нет…
} else {
// …то создаём его сами
    $file = fopen("data.json", "a+");
}
// 4/ Всё, что было в этом файле, переводим в массив, с которым умеет работать PHP. Таким способом у нас каждая JSON-запись будет храниться в отдельной ячейке массива.
$taskList = json_decode($file, true);
// 5. Добавляем новую запись в этот массив — кладём в него то, что пришло со страницы.
$taskList[] = array($data);
// 6. Записываем это всё обратно в файл и тут же читаем обратно из него — так мы убедимся, что запись прошла нормально и мы можем с этим работать.
// Записываем данные в файл…
file_put_contents('data.json', json_encode($taskList));
// …и сразу считываем их обратно
$file = file_get_contents('data.json'); // Открыть файл data.json
// 7. Отправляем всё содержимое файла на страницу, чтобы там увидеть, что сервер работает как нужно.
echo $file;
// Освобождаем память от переменных, которые нам пока не нужны
unset($file);
unset($taskList);

Обра­ти­те вни­ма­ние на послед­ние стро­ки в коде — мы осво­бож­да­ем память на сер­ве­ре, что­бы она не хра­ни­ла те пере­мен­ные, кото­рые нам уже не нуж­ны. Зачем это делать и что будет, если это­го не делать, — почи­тай­те в ста­тье про сбор­щи­ки мусо­ра.

Сра­зу вид­но, что сер­вер сохра­ня­ет каж­дые отправ­лен­ные дан­ные. Нам даже не при­шлось пока ниче­го менять в самом HTML-файле

Подготавливаем страницу

Теперь научим нашу стра­ни­цу пра­виль­но обра­ба­ты­вать ответ от сер­ве­ра и рабо­тать с полу­чен­ны­ми дан­ны­ми. Для это­го нам нуж­но:

  1. Пре­об­ра­зо­вать сырые дан­ные, полу­чен­ные от сер­ве­ра, в вид, понят­ный для JavaScript.
  2. Любым обра­зом поме­нять их зна­че­ния.
  3. Отпра­вить изме­нён­ные дан­ные назад на сер­вер.
  4. Полу­чить сра­зу их обрат­но и выве­сти на экран, что­бы убе­дить­ся, что сер­вер пра­виль­но понял все изме­не­ния.

Перед тем как рабо­тать с полу­чен­ны­ми дан­ны­ми в JavaScript, нам нуж­но их куда-то сохра­нить. Для это­го в самом нача­ле скрип­та заве­дём новую пере­мен­ную res, и доба­вим в функ­цию sendJSON() такую стро­ку:

// запоминаем данные, которые пришли с сервера res = this.responseText; // выводим то, что ответил нам сервер — так мы убедимся, что данные он получил правильно result.innerHTML = this.responseText;

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

Что­бы мы мог­ли отдель­но управ­лять все­ми изме­не­ни­я­ми, доба­вим в наш HTML-файл с про­шло­го про­ек­та новую кноп­ку рядом с пер­вой:

<!-- эта кнопка поменяет полученные данные и отправит их снова на сервер --!> <button onclick="editJSON()">Изменить JSON</button>

Сде­ла­ем функ­цию editJSON(), кото­рая сра­бо­та­ет по нажа­тию на эту кноп­ку. Рабо­тать она будет так:

  1. Возь­мёт сырые дан­ные от сер­ве­ра и пре­об­ра­зу­ет их в фор­мат, понят­ный для JavaScript.
  2. Най­дёт все име­на, посчи­та­ет коли­че­ство сим­во­лов в них и допи­шет это чис­ло к име­ни. Так мы пой­мём, что дан­ные мож­но менять как нам нуж­но.
  3. Пре­об­ра­зу­ет всё это обрат­но в JSON и отпра­вит на сер­вер.
  4. На вся­кий слу­чай сно­ва выве­дет резуль­тат на стра­ни­це, что­бы мы убе­ди­лись, что на сер­ве­ре лежат уже новые дан­ные.

Что­бы не писать код зано­во, мы ско­пи­ру­ем уже суще­ству­ю­щую функ­цию sendJSON(), пере­име­ну­ем её в editJSON() и доба­вим в неё то, что нам нуж­но. А перед этим в самом нача­ле скрип­та сно­ва доба­вим две новые пере­мен­ные:

  • newData, где мы будем хра­нить дан­ные в понят­ном фор­ма­те;
  • sendData, куда мы поме­стим новую JSON-строку перед отправ­кой на сер­вер.

Что мы делаем с JSON, полученным с сервера

Перед тем как пока­зать финаль­ный код, пояс­ним один момент. Мы пере­во­дим дан­ные с сер­ве­ра в фор­мат для JavaScript коман­дой

newData = JSON.parse(res).map(Object.values);

В резуль­та­те мы полу­ча­ем дву­мер­ный мас­сив, с кото­рым мож­но рабо­тать так:

newData[1][0].name = 'Привет'; newData[3][0].lastname = 'Привет';

Дело тут вот в чём: пер­вая циф­ра в квад­рат­ных скоб­ках отве­ча­ет за поряд­ко­вый номер JSON-записи, кото­рые мы отправ­ля­ли на сер­вер. Нуме­ра­ция идёт с нуля, как при­ня­то у про­грам­ми­стов.

Вто­рая циф­ра у нас все­гда 0, пото­му что хотя это и дву­мер­ный мас­сив, но каж­дая запись состо­ит все­го из одно­го эле­мен­та. Так как нуме­ра­ция с нуля, то мы и пишем 0.

После это­го через точ­ку идёт имя поля — у нас это name и lastname. Через них мы можем обра­щать­ся к кон­крет­ным зна­че­ни­ям и менять в них что угод­но.

Такая гро­мозд­кость полу­чи­лась из-за того, что мы реши­ли про­бле­му в лоб — про­сто пере­ве­ли дан­ные с помо­щью встро­ен­ных средств язы­ка. Это мож­но было сде­лать гораз­до кра­си­вее, но так — быст­рее.

Сам код новой функ­ции:

function editJSON() {
  // переводим данные от сервера в массив, понятный для JavaScript
  newData = JSON.parse(res).map(Object.values);
  // добавляем к каждому имени число с его длиной в скобках
  for (var i = newData.length - 1; i >= 0; i--) {
    newData[i][0].name = newData[i][0].name + '(' + newData[i][0].name.length + ')';
    console.log(newData[i][0].name);
  }
  // а вот сюда мы поместим ответ от сервера
  let result = document.querySelector('.result');
  // создаём новый экземпляр запроса XHR
  let xhr = new XMLHttpRequest();
  // адрес, куда мы отправим нашу JSON-строку
  let url = "http://mihailmaximov.ru/projects/json/edit.php";
  // открываем соединение
  xhr.open("POST", url, true);
  // устанавливаем заголовок — выбираем тип контента, который отправится на сервер, в нашем случае мы явно пишем, что это JSON
  // xhr.setRequestHeader("Content-Type", "application/json");
  // когда придёт ответ на наше обращение к серверу, мы его обработаем здесь
  xhr.onreadystatechange = function () {
    // если запрос принят и сервер ответил, что всё в порядке
    if (xhr.readyState === 4 && xhr.status === 200) {
      // выводим то, что ответил нам сервер — так мы убедимся, что данные он получил правильно
      result.innerHTML = this.responseText;
    }
  };
  // преобразуем наши новые данные в JSON-строку
  sendData = JSON.stringify(newData);
  // когда всё готово, отправляем JSON на сервер
  xhr.send(sendData);
}

Делаем новый PHP-обработчик на сервере

В скрип­те мы обра­ща­лись к фай­лу edit.php, кото­ро­го у нас ещё нет. Всё, что нуж­но от это­го фай­ла, — что­бы он сохра­нил то, что ему при­шло, и тут же отпра­вил это обрат­но, что­бы мы убе­ди­лись, что всё хоро­шо. Мы спе­ци­аль­но выно­сим такую про­стую функ­цию в отдель­ный файл, что­бы каж­дый из них отве­чал за свою область рабо­ты.

Сам файл доволь­но прост:

<?php
// на какие данные рассчитан этот скрипт
header("Content-Type: application/json");
// получаем данные от страницы
$data = file_get_contents("php://input");
// открываем наш файл с данными
$file = file_get_contents('data.json');
// записываем в него то, что пришло от страницы
file_put_contents('data.json', $data);
// тут же заново считываем все данные, чтобы убедиться, что всё записалось правильно
$file = file_get_contents('data.json');
// и сразу отправляем их на страницу, чтобы это увидел пользователь
echo $data;
// освобождаем память
unset($file);
Сра­зу видим резуль­тат — про­грам­ма посчи­та­ла коли­че­ство сим­во­лов в каж­дом име­ни, доба­ви­ла их и сохра­ни­ла все дан­ные на сер­ве­ре

Что дальше

Теперь у нас всё гото­во, что­бы пере­де­лать наши ста­рые про­ек­ты и научить их хра­нить свои дан­ные в обла­ке. Этим и зай­мём­ся.

Файл json.html

<!DOCTYPE html>
<html>

<head>
  <title>
    Отправляем JSON-данные на сервер
  </title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>

<body style="text-align:center;" id="body">
  <!-- заголовок -->
  <h1> Простая форма для проверки работы JSON </h1>
  <!-- делаем форму с полями ввода -->
  <p>
    <input type="text" id="name" placeholder="Ваше имя">
    <input type="text" id="lastname" placeholder="и фамилия">
    <!-- по нажатию на эту кнопку данные уйдут на сервер -->
    <button onclick="sendJSON()">Проверить JSON</button>
    <!-- эта кнопка поменяет полученные данные и отправит их снова на сервер -->
    <button onclick="editJSON()">Изменить JSON</button>
    <!-- а вот тут они появятся снова, но уже после обработки сервером -->
  <p class="result" style="color:blue"></p>
  </p>
  <!-- скрипт, который обработает нажатие на кнопку и отправит данные на сервер -->
  <script>
    var res;
    var newData;
    var sendData;
    // эта функция сработает при нажатии на кнопку
    function sendJSON() {
      // с помощью jQuery обращаемся к элементам на странице по их именам
      let name = document.querySelector('#name');
      let lastname = document.querySelector('#lastname');
      // а вот сюда мы поместим ответ от сервера
      let result = document.querySelector('.result');
      // создаём новый экземпляр запроса XHR
      let xhr = new XMLHttpRequest();
      // адрес, куда мы отправим нашу JSON-строку
      let url = "http://mihailmaximov.ru/projects/json/json.php";
      // открываем соединение
      xhr.open("POST", url, true);
      // устанавливаем заголовок — выбираем тип контента, который отправится на сервер, в нашем случае мы явно пишем, что это JSON
      // xhr.setRequestHeader("Content-Type", "application/json");
      // когда придёт ответ на наше обращение к серверу, мы его обработаем здесь
      xhr.onreadystatechange = function () {
        // если запрос принят и сервер ответил, что всё в порядке
        if (xhr.readyState === 4 && xhr.status === 200) {
          // запоминаем данные, которые пришли с сервера
          res = this.responseText;
          // выводим то, что ответил нам сервер — так мы убедимся, что данные он получил правильно
          result.innerHTML = this.responseText;
        }
      };
      // преобразуем наши данные в JSON-строку
      data = JSON.stringify({ "name": name.value, "lastname": lastname.value });
      // когда всё готово, отправляем JSON на сервер
      xhr.send(data);
    }
    function editJSON() {
      // переводим данные от сервера в массив, понятный для JavaScript
      newData = JSON.parse(res).map(Object.values);
      // добавляем к каждому имени число с его длиной в скобках
      for (var i = newData.length - 1; i >= 0; i--) {
        newData[i][0].name = newData[i][0].name + '(' + newData[i][0].name.length + ')';
        console.log(newData[i][0].name);
      }
      // а вот сюда мы поместим ответ от сервера
      let result = document.querySelector('.result');
      // создаём новый экземпляр запроса XHR
      let xhr = new XMLHttpRequest();
      // адрес, куда мы отправим нашу JSON-строку
      let url = "http://mihailmaximov.ru/projects/json/edit.php";
      // открываем соединение
      xhr.open("POST", url, true);
      // устанавливаем заголовок — выбираем тип контента, который отправится на сервер, в нашем случае мы явно пишем, что это JSON
      // xhr.setRequestHeader("Content-Type", "application/json");
      // когда придёт ответ на наше обращение к серверу, мы его обработаем здесь
      xhr.onreadystatechange = function () {
        // если запрос принят и сервер ответил, что всё в порядке
        if (xhr.readyState === 4 && xhr.status === 200) {
          // выводим то, что ответил нам сервер, — так мы убедимся, что данные он получил правильно
          result.innerHTML = this.responseText;
        }
      };
      // преобразуем наши новые данные JSON в строку
      sendData = JSON.stringify(newData);
      // когда всё готово, отправляем JSON на сервер
      xhr.send(sendData);
    }
  </script>
</body>

</html>

Файл json.php

<?php
// на какие данные рассчитан этот скрипт
header("Content-Type: application/json");
// 1. Получаем данные от страницы
$data = json_decode(file_get_contents("php://input"));
// 2. Проверяем, есть ли на сервере нужный нам файл с данными — json.data.
// Берём новую переменную и пишем в неё имя файла
$filename = 'data.json';
// 3. Если есть — запоминаем его содержимое, а если такого файла нет — создаём его отдельной командой.
if (file_exists($filename)) {
    // Если файл есть — открываем его и читаем данные
    $file = file_get_contents('data.json');
    // Если такого файла нет…
} else {
// …то создаём его сами
    $file = fopen("data.json", "a+");
}
// 4/ Всё, что было в этом файле, переводим в массив, с которым умеет работать PHP. Таким способом у нас каждая JSON-запись будет храниться в отдельной ячейке массива.
$taskList = json_decode($file, true);
// 5. Добавляем новую запись в этот массив — кладём в него то, что пришло со страницы.
$taskList[] = array($data);
// 6. Записываем это всё обратно в файл и тут же читаем обратно из него — так мы убедимся, что запись прошла нормально и мы можем с этим работать.
// Записываем данные в файл…
file_put_contents('data.json', json_encode($taskList));
// …и сразу считываем их обратно
$file = file_get_contents('data.json'); // Открыть файл data.json
// 7. Отправляем всё содержимое файла на страницу, чтобы там увидеть, что сервер работает как нужно.
echo $file;
// Освобождаем память от переменных, которые нам пока не нужны
unset($file);
unset($taskList);

Файл edit.php

<?php
// на какие данные рассчитан этот скрипт
header("Content-Type: application/json");
// получаем данные от страницы
$data = file_get_contents("php://input");
// открываем наш файл с данными
$file = file_get_contents('data.json');  
// записываем в него то, что пришло от страницы
file_put_contents('data.json',$data);  
// тут же заново считываем все данные, чтобы убедиться, что всё записалось правильно
$file = file_get_contents('data.json'); 
// и сразу отправляем их на страницу, чтобы это увидел пользователь
echo $data;
// освобождаем память 
unset($file);        
?>

Текст:
Миха­ил Поля­нин
Редак­тор:
Миха­ил Поля­нин
Кор­рек­тор:
Ири­на Михе­е­ва
Иллю­стра­ция:
Даня Бер­ков­ский
Вёрст­ка:
Мария Дро­но­ва
Соц­се­ти и вот это всё:
Вита­лий Вебер
Рас­ска­зал всем:
Вита­лий Вебер