Completed
Push — master ( d51012...8574e3 )
by Luis Ramón
02:50
created

BrowserController::getOrganizationTree()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 2
nop 2
1
<?php
2
/*
3
  ÁTICA - Aplicación web para la gestión documental de centros educativos
4
5
  Copyright (C) 2015-2017: Luis Ramón López López
6
7
  This program is free software: you can redistribute it and/or modify
8
  it under the terms of the GNU Affero General Public License as published by
9
  the Free Software Foundation, either version 3 of the License, or
10
  (at your option) any later version.
11
12
  This program is distributed in the hope that it will be useful,
13
  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
  GNU Affero General Public License for more details.
16
17
  You should have received a copy of the GNU Affero General Public License
18
  along with this program.  If not, see [http://www.gnu.org/licenses/].
19
*/
20
21
namespace AppBundle\Controller\Documentation;
22
23
use AppBundle\Entity\Documentation\Folder;
24
use AppBundle\Entity\Documentation\FolderRepository;
25
use AppBundle\Entity\ElementRepository;
26
use AppBundle\Entity\Organization;
27
use Doctrine\ORM\QueryBuilder;
28
use Pagerfanta\Adapter\DoctrineORMAdapter;
29
use Pagerfanta\Pagerfanta;
30
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
31
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
32
use Symfony\Component\HttpFoundation\Request;
33
34
class BrowserController extends Controller
35
{
36
    /**
37
     * @Route("/documentos/{id}/{page}", name="documentation", requirements={"page" = "\d+", "id" = "\d+"}, defaults={"page" = "1", "folder" = null}, methods={"GET"})
38
     */
39
    public function browseAction($page, $id = null, Request $request)
40
    {
41
        $organization = $this->get('AppBundle\Service\UserExtensionService')->getCurrentOrganization();
42
43
        $q = $request->get('q', null);
44
45
        $folder = (null === $id) ? $this->getRootFolder($organization) : $this->getFolder($organization, $id);
46
47
        if (null === $folder || $folder->getOrganization() !== $organization) {
48
            throw $this->createNotFoundException();
49
        }
50
51
        $pager = $this->getFolderEntriesPager($page, $folder, $q);
52
53
        $breadcrumb = $this->generateBreadcrumb($folder);
54
55
        return $this->render('documentation/list.html.twig', [
56
            'breadcrumb' => $breadcrumb,
57
            'elements' => $pager->getIterator(),
58
            'pager' => $pager,
59
            'current' => $folder,
60
            'tree' => $this->getOrganizationTree($this->getRootFolder($organization), $folder),
1 ignored issue
show
Bug introduced by
It seems like $this->getRootFolder($organization) can be null; however, getOrganizationTree() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
61
            'q' => $q,
62
            'domain' => 'element'
63
        ]);
64
    }
65
66
    /**
67
     * @param Organization $organization
68
     * @return Folder
69
     */
70 View Code Duplication
    private function getRootFolder($organization)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
71
    {
72
        /** @var FolderRepository $folderRepository */
73
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
74
75
        /** @var Folder|null $folder */
76
        $folder = $folderRepository->findOneBy(['organization' => $organization, 'parent' => null]);
77
78
        return $folder;
79
    }
80
81
    /**
82
     * @param Organization $organization
83
     * @param int $id
84
     * @return Folder
85
     */
86 View Code Duplication
    private function getFolder($organization, $id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
    {
88
        /** @var FolderRepository $folderRepository */
89
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
90
91
        /** @var Folder|null $folder */
92
        $folder = $folderRepository->findOneBy(['organization' => $organization, 'id' => $id]);
93
94
        return $folder;
95
    }
96
97
    /**
98
     * @param $page
99
     * @param Folder|null $folder
100
     * @param $q
101
     * @return Pagerfanta
102
     */
103
    private function getFolderEntriesPager($page, Folder $folder = null, $q)
104
    {
105
        /** @var ElementRepository $entriesRepository */
106
        $entriesRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Entry');
107
108
        // obtener las carpetas
109
        $folders = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder')->getChildren($folder, false);
110
111
        /** @var QueryBuilder $queryBuilder */
112
        $queryBuilder = $entriesRepository->createQueryBuilder('e')
113
            ->andWhere('e.folder IN (:folders)')
114
            ->setParameter('folders', $folders);
115
116
        if ($q) {
117
            $queryBuilder
118
                ->andWhere('e.name LIKE :tq')
119
                ->setParameter('tq', '%'.$q.'%');
120
        }
121
122
        $adapter = new DoctrineORMAdapter($queryBuilder, false);
123
        $pager = new Pagerfanta($adapter);
124
        $pager
125
            ->setMaxPerPage($this->getParameter('page.size'))
126
            ->setCurrentPage($q ? 1 : $page);
127
128
        return $pager;
129
    }
130
131
    /**
132
     * Returns breadcrumb that matches the folder (ignores root element)
133
     * @param Folder $folder
134
     * @param bool $ignoreLast
135
     * @return array
136
     */
137 View Code Duplication
    private function generateBreadcrumb(Folder $folder = null, $ignoreLast = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
    {
139
        $breadcrumb = [];
140
141
        if (null === $folder) {
142
            return null;
143
        }
144
145
        $item = $folder;
146
        while ($item->getParent()) {
147
            $entry = ['fixed' => $item->getName()];
148
            if ($item !== $folder || !$ignoreLast) {
149
                $entry['routeName'] = 'documentation';
150
                $entry['routeParams'] = ['id' => $item->getId()];
151
            }
152
            array_unshift($breadcrumb, $entry);
153
            $item = $item->getParent();
154
        }
155
        return $breadcrumb;
156
    }
157
158
    /**
159
     * Returns folder tree
160
     *
161
     * @param Folder $folder
162
     * @param Folder $current
163
     *
164
     * @return array
165
     */
166
    private function getOrganizationTree(Folder $folder, Folder $current = null)
167
    {
168
        /** @var FolderRepository $folderRepository */
169
        $folderRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Documentation\Folder');
170
        $children = $folderRepository->childrenHierarchy($folder);
171
172
        $parents = [];
173
        if ($current)
174
        {
175
            $parent = $current->getParent();
176
            while ($parent) {
177
                $parents[] = $parent->getId();
178
                $parent = $parent->getParent();
179
            }
180
        }
181
182
        $tree = $this->processChildren($children, $current ? $current->getId() : null, $parents);
1 ignored issue
show
Bug introduced by
It seems like $children defined by $folderRepository->childrenHierarchy($folder) on line 170 can also be of type string; however, AppBundle\Controller\Doc...ller::processChildren() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
183
184
        return $tree;
185
    }
186
187
    /**
188
     * Convert children array into a treeview array
189
     *
190
     * @param array $children
191
     * @param integer $currentId
192
     * @param array $parentsId
193
     * @return array
194
     */
195
    private function processChildren(array $children, $currentId = null, $parentsId = [])
196
    {
197
        $result = [];
198
        foreach($children as $child) {
199
            $item = [];
200
            $item['text'] = $child['name'];
201
            if ($currentId === $child['id']) {
202
                $item['state'] = ['selected' => true, 'expanded' => true];
203
            }
204
            if ($parentsId && in_array($child['id'], $parentsId)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parentsId of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
205
                $item['state'] = ['expanded' => true];
206
            }
207
            if (count($child['__children']) > 0) {
208
                $item['nodes'] = $this->processChildren($child['__children'], $currentId, $parentsId);
209
            } else {
210
                $item['icon'] = 'fa fa-folder';
211
            }
212
            $item['href'] = $this->generateUrl('documentation', ['id' => $child['id']]);
213
            $result[] = $item;
214
        }
215
216
        return $result;
217
    }
218
}
219