From 41240ef511c915223a6e13208fae22df3d2865e4 Mon Sep 17 00:00:00 2001
From: "a.shamavov" <a.shamavov@iqdev.digital>
Date: Fri, 12 Apr 2024 12:06:52 +0500
Subject: [PATCH] refactoring

---
 .gitignore                         |   1 +
 src/Action/Functions.php           | 141 +++++++++++++++++++++++++++++
 src/Controller/HomeController.php  |  55 ++++-------
 src/Validation/ArrayValidation.php |  27 ++++++
 4 files changed, 186 insertions(+), 38 deletions(-)
 create mode 100644 src/Action/Functions.php
 create mode 100644 src/Validation/ArrayValidation.php

diff --git a/.gitignore b/.gitignore
index 4daae38..930e1e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@
 /public/assets/
 /assets/vendor/
 ###< symfony/asset-mapper ###
+/.idea
\ No newline at end of file
diff --git a/src/Action/Functions.php b/src/Action/Functions.php
new file mode 100644
index 0000000..d453447
--- /dev/null
+++ b/src/Action/Functions.php
@@ -0,0 +1,141 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Action;
+
+class Functions
+{
+    /**
+     * Выполняет сортировку массива по убыванию цены
+     * @param array $array
+     * @return array
+     */
+
+    public function sortPrice(array $array): array
+    {
+        $prices = array_column($array, 'price');
+        $counts = array_column($array, 'count');
+        array_multisort(
+            $prices,
+            SORT_DESC,
+            $counts,
+            SORT_ASC, $array
+        );
+        return $array;
+    }
+
+    /**
+     * На выход должна вернуть отсортированный массив по ключу *price* DESC
+     * и во вторую очередь по *count* ASC:
+     * [['price'=>12, 'count'=>4],  ['price'=>10, 'count'=>2],  ['price'=>8, 'count'=>4],
+     * ['price'=>8, 'count'=>5],  ['price'=>5, 'count'=>5],]
+     */
+
+    /**
+     * Найдет элемент с указаным id
+     * @param array $array - массив, содержащий элементы со структурой
+     * [
+     *  'id' => 30,
+     *  'name' => 'Jhon',
+     *  'age' => 23,
+     * ]
+     * @param $id - ид искомого элемента
+     * @return ?array - найденный элемент
+     */
+
+    public function search(array $array, int $id): ?array
+    {
+        $rowId = array_search($id, array_column($array, 'id'), false);
+        if ($rowId !== false) {
+            return $array[$rowId];
+        }
+        return null;
+    }
+
+    /**
+     * Удалить дубликаты, оставив только уникальные значения
+     * @param array $array
+     * @return array
+     */
+
+    public function uniqElements(array $array): array
+    {
+        return array_unique($array, SORT_REGULAR);
+    }
+
+    /**
+     * Выходной массив:
+     * Array (
+     *   [0] => Array([0] => laravel, [1] => php)
+     *   [1] => Array([0] => codeigniter, [1] => php)
+     *   [3] => Array([0] => c++, [1] => java))
+     * )
+     */
+
+    /**
+     * Сгруппировать подразедлы в верхние разделы меню
+     * Дочерние элементы поместить в массив родителя с ключом submenu
+     * Значение под ключом depth определяет уровень раздела
+     * Массив $aMenu всегда начинается с элемента depth = 0,
+     * все последующие элементы с depth = 1 являются его дочерними
+     * элементами
+     * @param array $aMenu
+     * @return array
+     */
+
+    public function prepareMenu(array $aMenu): array
+    {
+        $result = [];
+        foreach ($aMenu as $arr) {
+            if ($arr['depth'] == 0) {
+                $result[] = array(
+                    'name' => $arr['name'],
+                    'depth' => $arr['depth'],
+                    'submenu' => []
+                );
+                continue;
+            }
+            $result[array_key_last($result)]['submenu'][] = array(
+                'name' => $arr['name'],
+                'depth' => $arr['depth'],
+            );
+        }
+        return $result;
+    }
+
+    /**
+     * Выходные данные:
+     * $aMenu = [
+     * [
+     *  'name' => 'Смартфоны и гаджеты',
+     *  'depth' => 0,
+     *  'submenu' => [
+     *      ['name' => 'Смартфоны, мобильные телефоны','depth' => 1,],
+     *      ['name' => 'Планшеты','depth' => 1,],
+     *      ['name' => 'Наушники и гарнитуры','depth' => 1,],],
+     * ],
+     * [
+     *  'name' => 'Компьютеры и ноутбуки',
+     *  'depth' => 0,
+     *  'submenu' => [
+     *      ['name' => 'Ноутбуки и аксессуары','depth' => 1,],
+     *      ['name' => 'Компьютеры и мониторы','depth' => 1,],
+     *      ['name' => 'Компьютерные комплектующие','depth' => 1,],]],
+     * [
+     *  'name' => 'Техника для дома',
+     *  'depth' => 0,
+     *  'submenu' => [
+     *      ['name' => 'Техника для уборки','depth' => 1,],
+     *      ['name' => 'Товары для ухода за одеждой','depth' => 1,],
+     *      ['name' => 'Аксессуары для техники','depth' => 1,],]
+     * ],
+     * [
+     *  'name' => 'Товары для дома и кухни',
+     *  'depth' => 0,
+     *  'submenu' => [
+     *      ['name' => 'Посуда','depth' => 1,],]],
+     * ];
+     */
+}
+
diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php
index ce5e43e..92bf56a 100644
--- a/src/Controller/HomeController.php
+++ b/src/Controller/HomeController.php
@@ -2,52 +2,31 @@
 
 namespace App\Controller;
 
+use App\Action\Functions;
+use App\Validation\ArrayValidation;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Attribute\Route;
 
 class HomeController extends AbstractController
 {
-    private function prepareMenu(array $aMenu): array {
-        $result = [];
-        foreach ($aMenu as $arr) {
-            if ($arr['depth'] === 0) {
-                $result[] = array(
-                    'name' => $arr['name'],
-                    'depth' => $arr['depth'],
-                    'submenu' => []
-                );
-                continue;
-            }
-            $result[array_key_last($result)]['submenu'][] = array(
-                'name' => $arr['name'],
-                'depth' => $arr['depth'],
-            );
-        }
-        return $result;
-    }
-
+    private Functions $functions;
 
-    #[Route('/', name: 'home')]
-    public function home(): Response
+    public function __construct(Functions $functions)
     {
-        $aMenu = [
-            ['name' => 'Смартфоны и гаджеты','depth' => 0,],
-            ['name' => 'Смартфоны, мобильные телефоны','depth' => 1,],
-            ['name' => 'Планшеты','depth' => 1,],
-            ['name' => 'Наушники и гарнитуры','depth' => 1,],
-            ['name' => 'Компьютеры и ноутбуки','depth' => 0,],
-            ['name' => 'Ноутбуки и аксессуары','depth' => 1,],
-            ['name' => 'Компьютеры и мониторы','depth' => 1,],
-            ['name' => 'Компьютерные комплектующие','depth' => 1,],
-            ['name' => 'Техника для дома','depth' => 0,],
-            ['name' => 'Техника для уборки','depth' => 1,],
-            ['name' => 'Товары для ухода за одеждой','depth' => 1,],
-            ['name' => 'Аксессуары для техники','depth' => 1,],
-            ['name' => 'Товары для дома и кухни','depth' => 0,],
-            ['name' => 'Посуда','depth' => 1,],
-        ];
+        $this->functions = $functions;
+    }
 
-        return $this->render('home.html.twig', ['menu' => $this->prepareMenu($aMenu)]);
+    #[Route('/', name: 'home', methods: ['POST'])]
+    public function home(Request $request): Response
+    {
+        $array = $request->get('arr');
+        $errors = ArrayValidation::validate($array);
+        if (count($errors) > 0) {
+            return new Response((string)$errors);
+        }
+        $result = $this->functions->prepareMenu($array);
+        return $this->json($result);
     }
 }
diff --git a/src/Validation/ArrayValidation.php b/src/Validation/ArrayValidation.php
new file mode 100644
index 0000000..83567bd
--- /dev/null
+++ b/src/Validation/ArrayValidation.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Validation;
+
+use Symfony\Component\Validator\Validation;
+use Symfony\Component\Validator\Constraints as Assert;
+use Symfony\Component\Validator\ConstraintViolationListInterface;
+
+class ArrayValidation
+{
+    public static function validate(array $array): ConstraintViolationListInterface
+    {
+        $validator = Validation::createValidator();
+        $constraints = new Assert\Optional([
+            new Assert\Collection([
+                new Assert\Optional([
+                    new Assert\Type('array'),
+                    new Assert\Collection([
+                        'name' => new Assert\Type('string'),
+                        'depth' => new Assert\Type('int'),
+                    ])
+                ])
+            ])
+        ]);
+        return $validator->validate($array, $constraints);
+    }
+}
\ No newline at end of file
-- 
GitLab