DatabaseQueryRetriever::retrieveAll()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 17
c 0
b 0
f 0
dl 0
loc 26
rs 9.7
cc 3
nc 3
nop 3
1
<?php
2
3
namespace W2w\Laravel\Apie\Plugins\Illuminate\DataLayers;
4
5
use Illuminate\Database\DatabaseManager;
6
use ReflectionClass;
7
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
8
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
9
use W2w\Laravel\Apie\Exceptions\ApiResourceContextException;
10
use W2w\Laravel\Apie\Exceptions\FileNotFoundException;
11
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterFromMetadataTrait;
12
use W2w\Lib\Apie\Core\SearchFilters\SearchFilterRequest;
13
use W2w\Lib\Apie\Exceptions\ResourceNotFoundException;
14
use W2w\Lib\Apie\Interfaces\ApiResourceRetrieverInterface;
15
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
16
use W2w\Lib\Apie\Interfaces\SearchFilterProviderInterface;
17
18
/**
19
 * Does a SQL query and maps the output to a domain object. The result set should have an id returned to retrieve
20
 * single records.
21
 */
22
class DatabaseQueryRetriever implements ApiResourceRetrieverInterface, SearchFilterProviderInterface
23
{
24
    use SearchFilterFromMetadataTrait;
25
26
    private $db;
27
28
    private $serializer;
29
30
    /**
31
     * @param DatabaseManager       $db
32
     * @param NormalizerInterface   $normalizer
33
     * @param DenormalizerInterface $denormalizer
34
     */
35
    public function __construct(DatabaseManager $db, ResourceSerializerInterface $serializer)
36
    {
37
        $this->db = $db;
38
        $this->serializer = $serializer;
39
    }
40
41
    /**
42
     * Retrieves a single resource.
43
     *
44
     * @param  string $resourceClass
45
     * @param  mixed  $id
46
     * @param  array  $context
47
     * @return array|object
48
     */
49
    public function retrieve(string $resourceClass, $id, array $context)
50
    {
51
        $query = $this->getFindQuery($resourceClass, $context);
52
        if (empty($query)) {
53
            throw new ApiResourceContextException($resourceClass, 'a query_single or query_single_file');
54
        }
55
        $result = $this->db->select($this->db->raw($query), ['id' => $id]);
56
        if (empty($result)) {
57
            throw new ResourceNotFoundException($id);
58
        }
59
60
        return $this->serializer->hydrateWithReflection((array) $result[0], $resourceClass);
61
    }
62
63
    /**
64
     * Retrieves all results.
65
     *
66
     * @param  string               $resourceClass
67
     * @param  array                $context
68
     * @param   SearchFilterRequest $searchFilterRequest
69
     * @return iterable
70
     */
71
    public function retrieveAll(string $resourceClass, array $context, SearchFilterRequest $searchFilterRequest): iterable
72
    {
73
        $query = $this->getAllQuery($resourceClass, $context);
74
75
        if (empty($query)) {
76
            throw new ApiResourceContextException($resourceClass, 'a query or query_file');
77
        }
78
        $parameters = [
79
            'offset' => $searchFilterRequest->getOffset(),
80
            'limit' => $searchFilterRequest->getNumberOfItems()
81
        ];
82
        $count = 0;
83
        $query = 'SELECT * FROM (' . $query . ')  AS subquery WHERE 1 = 1';
84
        foreach ($searchFilterRequest->getSearches() as $name => $value) {
85
            $query .= ' AND `' . $name . '` = :var' . $count;
86
            $parameters['var' . $count] = $value;
87
            $count++;
88
        }
89
90
        $result = $this->db->select(
91
            $this->db->raw(
92
                $query . ' LIMIT :offset, :limit'
93
            ),
94
            $parameters
95
        );
96
        return $this->serializer->hydrateWithReflection((array) $result, $resourceClass . '[]');
97
    }
98
99
    /**
100
     * Returns the query to retrieve all rows.
101
     *
102
     * @param  string $resourceClass
103
     * @param  array  $context
104
     * @return string
105
     */
106
    private function getAllQuery(string $resourceClass, array $context): ?string
107
    {
108
        if (!empty($context['query_file'])) {
109
            $classNameFile = (new ReflectionClass($resourceClass))->getFileName();
110
            if (!$classNameFile) {
111
                throw new FileNotFoundException($resourceClass);
112
            }
113
            $filename = dirname($classNameFile) . DIRECTORY_SEPARATOR . $context['query_file'];
114
            if (!file_exists($filename)) {
115
                throw new FileNotFoundException($filename);
116
            }
117
            $context['query'] = file_get_contents($filename);
118
        }
119
120
        return $context['query'] ?? null;
121
    }
122
123
    /**
124
     * Returns the query to retrieve a single resource.
125
     *
126
     * @param  string $resourceClass
127
     * @param  array  $context
128
     * @return string
129
     */
130
    private function getFindQuery(string $resourceClass, array $context): ?string
131
    {
132
        if (!empty($context['query_single_file'])) {
133
            $classNameFile = (new ReflectionClass($resourceClass))->getFileName();
134
            if (!$classNameFile) {
135
                throw new FileNotFoundException($resourceClass);
136
            }
137
            $filename = dirname($classNameFile) . DIRECTORY_SEPARATOR . $context['query_single_file'];
138
            if (!file_exists($filename)) {
139
                throw new FileNotFoundException($filename);
140
            }
141
            $context['query_single'] = file_get_contents($filename);
142
        }
143
        if (empty($context['query_single'])) {
144
            $allQuery = $this->getAllQuery($resourceClass, $context);
145
            if (!empty($allQuery)) {
146
                return 'SELECT * FROM (' . $allQuery . ')  AS subquery WHERE id = :id';
147
            }
148
        }
149
150
        return $context['query_single'] ?? null;
151
    }
152
}
153