Passed
Push — master ( efdc62...6094bd )
by WEBEWEB
13:17
created

RepositoryService::findOneByEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the core-bundle package.
5
 *
6
 * (c) 2022 WEBEWEB
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 WBW\Bundle\CoreBundle\Service;
13
14
use Doctrine\ORM\Mapping\ClassMetadata;
15
use PDO;
16
use Throwable;
17
use WBW\Library\Symfony\Model\RepositoryDetail;
18
use WBW\Library\Symfony\Model\RepositoryReport;
19
use WBW\Library\Symfony\Model\RepositoryReportInterface;
20
use WBW\Library\Types\Helper\ArrayHelper;
21
22
/**
23
 * Repository service.
24
 *
25
 * @author webeweb <https://github.com/webeweb>
26
 * @package WBW\Bundle\CoreBundle\Service
27
 */
28
class RepositoryService implements RepositoryServiceInterface {
29
30
    use StatementServiceTrait {
31
        setStatementService as public;
32
    }
33
34
    /**
35
     * Service name.
36
     *
37
     * @var string
38
     */
39
    const SERVICE_NAME = "wbw.core.service.repository";
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function findAll(): array {
45
46
        /** @var RepositoryReportInterface[] $reports */
47
        $reports = [];
48
49
        /** @var ClassMetadata[] $allMetadata */
50
        $allMetadata = $this->getStatementService()->getEntityManager()->getMetadataFactory()->getAllMetadata();
51
        foreach ($allMetadata as $current) {
52
53
            if (false === $current->isMappedSuperclass) {
54
                $reports[] = $this->findReport($current);
55
            }
56
        }
57
58
        usort($reports, static::usortRepositoryReportCallback());
59
60
        return $reports;
61
    }
62
63
    /**
64
     * Find all repository details.
65
     *
66
     * @param ClassMetadata $classMetadata The class metadata.
67
     * @return RepositoryDetail[] Returns the repository details.
68
     * @throws Throwable Throws an exception if an error occurs.
69
     */
70
    protected function findDetails(ClassMetadata $classMetadata): array {
71
72
        /** @var RepositoryDetail[] $models */
73
        $models = [];
74
75
        foreach ($classMetadata->getFieldNames() as $current) {
76
77
            $fieldMapping = $classMetadata->getFieldMapping($current);
78
            if (false === in_array($fieldMapping["type"], ["string", "text"])) {
79
                continue;
80
            }
81
82
            $params = [
83
                $classMetadata->getTableName(),
84
                $classMetadata->getName(),
85
                $fieldMapping["columnName"],
86
                $fieldMapping["fieldName"],
87
                ArrayHelper::get($fieldMapping, "length", -1),
88
            ];
89
90
            $models[] = $this->newRepositoryDetail($params[0], $params[1], $params[2], $params[3], $params[4]);
91
        }
92
93
        return $models;
94
    }
95
96
    /**
97
     * Find one repository report.
98
     *
99
     * @param callable $criteria The criteria.
100
     * @param string $value The value.
101
     * @return RepositoryReportInterface|null Returns the repository report.
102
     * @throws Throwable Throws an exception if an error occurs.
103
     */
104
    protected function findOneBy(callable $criteria, string $value): ?RepositoryReportInterface {
105
106
        /** @var ClassMetadata[] $allMetadata */
107
        $allMetadata = $this->getStatementService()->getEntityManager()->getMetadataFactory()->getAllMetadata();
108
        foreach ($allMetadata as $current) {
109
110
            if (false === $current->isMappedSuperclass && $value === $criteria($current)) {
111
                return $this->findReport($current);
112
            }
113
        }
114
115
        return null;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function findOneByEntity(string $entity): ?RepositoryReportInterface {
122
123
        $criteria = function(ClassMetadata $classMetadata) {
124
            return $classMetadata->getName();
125
        };
126
127
        return $this->findOneBy($criteria, $entity);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function findOneByTable(string $table): ?RepositoryReportInterface {
134
135
        $criteria = function(ClassMetadata $classMetadata) {
136
            return $classMetadata->getTableName();
137
        };
138
139
        return $this->findOneBy($criteria, $table);
140
    }
141
142
    /**
143
     * Find one repository report.
144
     *
145
     * @param ClassMetadata $classMetadata The class metadata.
146
     * @return RepositoryReportInterface Returns the repository report.
147
     * @throws Throwable Throws an exception if an error occurs.
148
     */
149
    protected function findReport(ClassMetadata $classMetadata): RepositoryReportInterface {
150
151
        $query  = "SELECT COUNT(*) FROM {$classMetadata->getTableName()}";
152
        $result = $this->getStatementService()->executeQuery($query, []);
153
154
        $count = intval($result->fetchOne());
155
156
        $model = new RepositoryReport();
157
        $model->setTable($classMetadata->getTableName());
158
        $model->setEntity($classMetadata->getName());
159
        $model->setCount($count);
160
161
        $details = $this->findDetails($classMetadata);
162
        foreach ($details as $current) {
163
164
            if (null !== $current) {
165
                $model->addDetail($current->setRepositoryReport($model));
166
            }
167
        }
168
169
        return $model;
170
    }
171
172
    /**
173
     * Creates a repository detail.
174
     *
175
     * @param string $table The table.
176
     * @param string $entity The entity.
177
     * @param string $column The column.
178
     * @param string $field The field.
179
     * @param int $available The available.
180
     * @return RepositoryDetail Returns the repository detail.
181
     * @throws Throwable Throws an exception if an error occurs.
182
     */
183
    protected function newRepositoryDetail(string $table, string $entity, string $column, string $field, int $available): RepositoryDetail {
184
185
        $template = $this->getStatementService()->readStatementFile(__DIR__ . "/RepositoryService.sql");
186
187
        $searches = ["{{ table }}", "{{ column }}"];
188
        $replaces = [$table, $column];
189
190
        $sql = str_replace($searches, $replaces, $template);
191
192
        $stmt = $this->getStatementService()->prepareStatement($sql, [
193
            ":available" => [$available, PDO::PARAM_INT],
194
            ":column"    => [$column, PDO::PARAM_STR],
195
            ":entity"    => [$entity, PDO::PARAM_STR],
196
            ":field"     => [$field, PDO::PARAM_STR],
197
            ":table"     => [$table, PDO::PARAM_STR],
198
        ]);
199
200
        $result = $stmt->executeQuery();
201
202
        $row = $result->fetchAllAssociative()[0];
203
204
        $model = new RepositoryDetail();
205
        $model->setAvailable(intval($row["available"]));
206
        $model->setAverage(floatval($row["average"]));
207
        $model->setColumn($row["column"]);
208
        $model->setField($row["field"]);
209
        $model->setMaximum(intval($row["maximum"]));
210
        $model->setMinimum(intval($row["minimum"]));
211
212
        return $model;
213
    }
214
215
    /**
216
     * Usort repository report callback.
217
     *
218
     * @return callable Returns the usort repository report callback.
219
     */
220
    protected static function usortRepositoryReportCallback(): callable {
221
222
        return function(RepositoryReport $a, RepositoryReport $b): int {
223
            return strcmp($a->getTable(), $b->getTable());
0 ignored issues
show
Bug introduced by
It seems like $b->getTable() can also be of type null; however, parameter $string2 of strcmp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
            return strcmp($a->getTable(), /** @scrutinizer ignore-type */ $b->getTable());
Loading history...
Bug introduced by
It seems like $a->getTable() can also be of type null; however, parameter $string1 of strcmp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
            return strcmp(/** @scrutinizer ignore-type */ $a->getTable(), $b->getTable());
Loading history...
224
        };
225
    }
226
}
227