Админка магазина на vue.js. Урок 4. Правим код под новое REST API и расследуем багу

февраль 7 , 2019
Предыдущая статья Следующая статья Исходники

Продолжаем работать над админкой vue. Перед тем, как разбивать наше приложение на компоненты, решил написать один промежуточный урок. В нем мы сделаем две вещи.

Во-первых, перейдем на новое серверное rest api, которое мы реализовали в третьем уроке.

Во-вторых, разберем багу, которая закралась в наш код. Бага интересна тем, что ее не заметили ни я, ни читатели. Нашел случайно, когда работал над третьим уроком с новым rest api.

Но обо всем по порядку.


Переводим клиент на новое REST API

Вспомним, что мы делали в первых двух уроках. Мы работали с тремя сущностями: товары, категории и бренды. Товары и бренды мы получали с сервера одним запросом, а категории вообще вбивали руками. Теперь с новым api мы можем работать с каждой из сущностей по отдельности. Плюс меня немного нервировали эти странные названия полей вроде good_id и good. Сейчас есть сущность товар (product), и у нее есть поля id и title (названия). Точно такие же поля и у брендов с категориями. Универсально и легко запоминается.

Первое, что нужно сделать, это избавиться от здоровенного запроса GET /scripts/catalog.php?needs_data=brands, и вместо него использовать культурные GET products (brands или categories) Для этого мы идем в app.js, в метод mount и меняем его старое содержимое, вот такое

    mounted: function() {
        axios
            .get('/scripts/catalog.php?needs_data=brands')
            .then(response => {
                this.products = response.data.data.goods;
                this.brands = response.data.data.brands;
                this.minPrice = this.getMinPrice();
                this.maxPrice = this.getMaxPrice();
            });
    }

на новое, такое

    mounted: function() {
        // Получение товаров
        axios
            .get('/admin/api/v1/products')
            .then(response => {
                this.products = response.data.records;
                this.minPrice = this.getMinPrice();
                this.maxPrice = this.getMaxPrice();
            });

        // Получение категорий
        axios
            .get('/admin/api/v1/categories')
            .then(response => {
                this.categories = response.data.records;
            });

        // Получение брендов
        axios
            .get('/admin/api/v1/brands')
            .then(response => {
                this.brands = response.data.records;
            });
    }

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

Найдем в data место, где мы набивали руками категории и заменим это на categories: []. Все, намного приятнее, чем старое

    categories: [
        { id: 1, category: 'Ноутбуки' },
        { id: 2, category: 'Смартфоны' },
        { id: 3, category: 'Видеокарты' }
    ]

По сути, вся работа уже сделана, нам остается только пробежаться по коду и заменить некоторые старые поля на новые в соответствии с api.

Вот список изменений.


app.js

1. В компоненте ProductItem меняем good_id на просто id и good меняем на title.
2. В data sortRules меняем good_id на просто id, то же самое в selectSort
3. Вычисляемое поле filteredProducts, в нем меняем category_id на categoryId, brand на brandId и good на title
4. В методе clear тоже меняем good_id на id.


index.html

Здесь замен еще меньше

1. category.category меняем на category.title
2. Меняем старое

        
    
на новое
        
    
3. Меняем :key="product.good_id" на :key="product.id"

Все изменения в обновленных исходниках

По коду все, теперь поговорим про багу, которую я упоминал в начале статьи.


Правим багу

Открою секрет: мы ее уже поправили, сами того не зная. А в чем эта бага состояла? Давайте вместе и посмотрим :)

Откройте демо предыдущего урока - https://shop.webdevkin.ru/admin/vue/lesson2-filters/. Смотрите, мы высчитали минимальную и максимальную цены, как 11000 и 70000. И в таблице у нас выведено 12 товаров. Обратите внимание на их айдишники: с 1 по 11 и 14й. А где же 12й и 13й? Просто пропущены в базе? Нет. Они возвращаются нам с бекенда и хранятся в памяти, в массиве data.products. Можете убедиться, поставьте минимальную цену товаров 0 и увидите, что 12й и 13й товары появились. Их стоимости 2000 и 6000. Как так, при загрузке же мы вычислили минимальную цену в 11000, почему не 2000?

Вот код, он верный.

    getMinPrice: function() {
        return Number(_.minBy(this.products, 'price').price);
    }

Дело не в этом коде. Посмотрите, что возвращает запрос, которым мы пользовались до этого урока - https://shop.webdevkin.ru/scripts/catalog.php?needs_data=brands. Обратите внимание, в каком виде у нас возвращается цена товара. Это строка. В этом и заключалась наша ошибка.

Ведь функция lodash minBy использует простое сравнение значений. 2000 < 11000, но "2000" > "11000". Именно поэтому, когда предыдущий запрос возвращал строки, мы получали неправильный результат. А в новой версии мы не поленились и преобразовали данные к нужному типу еще на сервере. Помните, как мы упарывались с подобными штуками?

    return array(
        'id' => $id,
        'title' => $item['good'],
        'categoryId' => (int)$item['category_id'],
        'brandId' => (int)$item['brand_id'],
        'brand' => $item['brand'],
        'price' => (int)$item['price'],
        'rating' => (int)$item['rating']
    );

Все числовые значения мы старательно преобразовали к int и тем самым починили немаленькую багу. Представьте, сколько товаров недосчитались бы мы в нашей админке, если бы и дальше работали со строками.

Мораль такая: следите за типами данных, несмотря на то, что яваскрипт очень лоялен к таким фокусам. Проверяйте данные, которые возвращаются с сервера. Пинайте бэкендщиков, чтобы возвращали числа в нужном формате. Не забывайте делать это, если сами пишете бэкенд. Если нет возможности изменить данные с сервера, преобразовывайте их на клиенте. Так вы сможете избежать таких глупых багов, какой случился у меня :-)

На сегодня все, готовлю следующий урок. Подписывайтесь на рассылку, чтобы первыми узнавать о выходе новых статей ;-)

Предыдущие уроки админки на vue.js

Предыдущая статья Следующая статья Исходники
Как Вам статья? Оцените!
Понравилась статья? Поделись с другими!
Подписка на новые статьи
Подписаться