Add alias member variable
[treebundle] / Repository / ElementRepository.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys TreeBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\TreeBundle\Repository;
13
14 use Doctrine\ORM\Query\ResultSetMapping;
15
16 use Rapsys\TreeBundle\Repository;
17
18 /**
19 * Element repository
20 */
21 class ElementRepository extends Repository {
22 /**
23 * Find element as array
24 *
25 * @param ?integer $uid The user id
26 * @param integer $id The element id
27 * @param string $path The element path
28 * @return ?array The element array
29 */
30 public function findOneByUidIdPathAsArray(?int $uid, int $id, string $path): ?array {
31 //Set the request
32 $req = <<<SQL
33 SELECT
34 e.id,
35 e.path,
36 GREATEST(a.created, e.created) AS created,
37 GREATEST(a.updated, e.updated) AS updated,
38 GREATEST(a.created, e.created, a.updated, e.updated) AS modified,
39 a.id AS a_id,
40 a.path AS a_path,
41 a.slug AS a_slug
42 FROM Rapsys\TreeBundle\Entity\Element AS e
43 JOIN Rapsys\TreeBundle\Entity\Album AS a ON (a.id = e.album_id)
44 WHERE e.id = :id AND e.path = SUBSTR(:path, 1, LENGTH(e.path)) AND e.user_id IN (NULL, :uid)
45 SQL;
46
47 //Replace bundle entity name by table name
48 $req = $this->replace($req);
49
50 //Get result set mapping instance
51 $rsm = new ResultSetMapping();
52
53 //Declare all fields
54 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
55 //addScalarResult($sqlColName, $resColName, $type = 'string');
56 $rsm->addScalarResult('id', 'id', 'integer')
57 ->addScalarResult('path', 'path', 'string')
58 ->addScalarResult('created', 'created', 'datetime')
59 ->addScalarResult('updated', 'updated', 'datetime')
60 ->addScalarResult('modified', 'modified', 'datetime')
61 ->addScalarResult('a_id', 'a_id', 'integer')
62 ->addScalarResult('a_path', 'a_path', 'string')
63 ->addScalarResult('a_slug', 'a_slug', 'string')
64 ->addIndexByScalar('id');
65
66 //Get result
67 $result = $this->_em
68 ->createNativeQuery($req, $rsm)
69 ->setParameter('uid', $uid)
70 ->setParameter('id', $id)
71 ->setParameter('path', $path)
72 ->getOneOrNullResult();
73
74 //Check result
75 if (
76 //Without result
77 $result === null ||
78 //Without realpath
79 !($realpath = realpath($result['a_path'].($path?DIRECTORY_SEPARATOR.$path:''))) ||
80 //Realpath not matching element path
81 $result['a_path'].($result['path']?DIRECTORY_SEPARATOR.$result['path']:'') !== substr($realpath, 0, strlen($result['a_path'].($result['path']?DIRECTORY_SEPARATOR.$result['path']:'')))
82 ) {
83 //Return null
84 return null;
85 }
86
87 //Set breadcrumbs
88 $breadcrumbs = [
89 //Add album
90 [
91 'name' => ucfirst($result['a_slug']),
92 'link' => $alink = $this->router->generate('rapsystree_album', [ 'id' => $result['a_id'], 'path' => '', 'slug' => $result['a_slug'] ])
93 ],
94 //Add element
95 [
96 'name' => $name = trim(str_replace('/', ' / ', '/'.$result['path'])),
97 'link' => $link = $this->router->generate('rapsystree_element', [ 'id' => $result['id'], 'path' => $result['path'] ])
98 ]
99 ];
100
101 //Set base
102 $base = $result['path'];
103
104 //Iterate on intermediate breadcrumbs
105 foreach(array_slice(explode('/', substr($realpath, strlen($result['a_path'].($result['path']?DIRECTORY_SEPARATOR.$result['path']:'')))), 1) as $value) {
106 //Add breadcrumb
107 $breadcrumbs[] = [
108 'name' => ($base == '' ? '' : '/ ').$value,
109 'link' => $this->router->generate('rapsystree_element', [ 'id' => $result['id'], 'path' => ($base .= ($base == '' ? '' : '/').$value) ])
110 ];
111 }
112
113 //Set directories
114 $directories = [];
115
116 //Set files
117 $files = [];
118
119 //Set file
120 $file = [];
121
122 //With directory
123 if (is_dir($realpath)) {
124 //Iterate on directory
125 foreach(array_diff(scandir($realpath), ['.', '..']) as $item) {
126 //TODO: exclude .svn, .git, .passwd, .*.un~, etc... (if not already protected by haproxy/apache)
127
128 //Check item
129 if (
130 //Without item realpath
131 !($itempath = realpath($realpath.DIRECTORY_SEPARATOR.$item)) ||
132 //Item realpath not matching element path
133 $result['a_path'].($result['path']?DIRECTORY_SEPARATOR.$result['path']:'') !== substr($itempath, 0, strlen($result['a_path'].($result['path']?DIRECTORY_SEPARATOR.$result['path']:'')))
134 ) {
135 //Skip
136 continue;
137 }
138
139 //With directory
140 if (is_dir($itempath)) {
141 //Append directory
142 $directories[$item.'/'] = $this->router->generate('rapsystree_element', [ 'id' => $result['id'], 'path' => $path.'/'.$item ]);
143 //With file
144 } elseif (is_file($itempath)) {
145 //Get file infos
146 $fileinfos = $this->file->infos($itempath);
147
148 //Append file
149 $files[$fileinfos['name']] = [
150 //Set link
151 'link' => $this->router->generate('rapsystree_element', [ 'id' => $result['id'], 'path' => $path.'/'.$item ])
152 ]+$fileinfos;
153 //With unknown type
154 } else {
155 //Throw 404
156 throw new \Exception('Unknown element item type');
157 }
158 }
159 //With file
160 } elseif (is_file($realpath)) {
161 //Get file infos
162 $fileinfos = $this->file->infos($realpath);
163
164 //Append file
165 $file = [
166 //Set link
167 'link' => $this->router->generate('rapsystree_element', [ 'id' => $result['id'], 'path' => $path ])
168 ]+$fileinfos;
169 //With unknown type
170 } else {
171 //Throw 404
172 throw new \Exception('Unknown element type');
173 }
174
175 //Return element
176 return [
177 'id' => $result['id'],
178 'name' => $name,
179 'path' => $result['path'],
180 'realpath' => $realpath,
181 'link' => $link,
182 'created' => $result['created'],
183 'updated' => $result['updated'],
184 'modified' => $result['modified'],
185 'album' => [
186 'id' => $result['a_id'],
187 'path' => $result['a_path'],
188 'slug' => $result['a_slug'],
189 'link' => $alink
190 ],
191 'breadcrumbs' => $breadcrumbs,
192 'directories' => $directories,
193 'files' => $files,
194 'file' => $file
195 ];
196 }
197 }