Completed
Push — master ( 2a9f41...e2e9c6 )
by Filipe
02:27
created

HasAndBelongsToMany   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 54.95%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 10
c 3
b 0
f 2
lcom 1
cbo 13
dl 0
loc 190
ccs 50
cts 91
cp 0.5495
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getRelatedForeignKey() 0 8 2
A getRelationTable() 0 14 2
A load() 0 22 1
A add() 0 20 1
A remove() 0 6 1
B deleteRelation() 0 25 1
A getConditions() 0 10 1
A addJoinTable() 0 14 1
1
<?php
2
3
/**
4
 * This file is part of slick/orm package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Orm\Mapper\Relation;
11
use Slick\Common\Utils\Text;
12
use Slick\Database\Sql;
13
use Slick\Database\Sql\Select;
14
use Slick\Orm\Entity\EntityCollectionInterface;
15
use Slick\Orm\EntityInterface;
16
use Slick\Orm\Event\EntityAdded;
17
use Slick\Orm\Event\EntityRemoved;
18
19
/**
20
 * HasAndBelongsToMany relation
21
 * 
22
 * @package Slick\Orm\Mapper\Relation
23
 * @author  Filipe Silva <[email protected]>
24
 */
25
class HasAndBelongsToMany extends HasMany
26
{
27
28
    /**
29
     * @readwrite
30
     * @var string
31
     */
32
    protected $relatedForeignKey;
33
34
    /**
35
     * @readwrite
36
     * @var string
37
     */
38
    protected $relationTable;
39
40
    /**
41
     * Gets the related foreign key
42
     * 
43
     * @return string
44
     */
45 4
    public function getRelatedForeignKey()
46
    {
47 4
        if (null == $this->relatedForeignKey) {
48 4
            $name = $this->getParentTableName();
49 4
            $this->relatedForeignKey = "{$this->normalizeFieldName($name)}_id";
50 4
        }
51 4
        return $this->relatedForeignKey;
52
    }
53
54
    /**
55
     * Gets the related table
56
     * 
57
     * @return string
58
     */
59 4
    public function getRelationTable()
60
    {
61 4
        if (is_null($this->relationTable)) {
62 4
            $parentTable = $this->getParentTableName();
63 4
            $table = $this->getEntityDescriptor()->getTableName();
64 4
            $names = [$parentTable, $table];
65 4
            asort($names);
66 4
            $first = array_shift($names);
67 4
            $tableName = $this->normalizeFieldName($first);
68 4
            array_unshift($names, $tableName);
69 4
            $this->relationTable = implode('_', $names);
70 4
        }
71 4
        return $this->relationTable;
72
    }
73
74
    /**
75
     * Loads the entity or entity collection for this relation
76
     *
77
     * @param EntityInterface $entity
78
     *
79
     * @return EntityCollectionInterface|EntityInterface[]
80
     */
81 2
    public function load(EntityInterface $entity)
82
    {
83 2
        $repository = $this->getParentRepository();
84
85 2
        $query = $repository->find()
86 2
            ->where($this->getConditions($entity))
0 ignored issues
show
Documentation introduced by
$this->getConditions($entity) is of type array<string|integer,array<string|integer,*>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
87 2
            ->limit($this->limit);
88 2
        $this->addJoinTable($query);
89 2
        $this->checkConditions($query)
90 2
            ->checkOrder($query);
91
92
        /** @var EntityCollectionInterface $collection */
93 2
        $collection = $query->all();
94
95
        $collection
96 2
            ->setParentEntity($entity)
97 2
            ->getEmitter()
98 2
            ->addListener(EntityAdded::ACTION_ADD, [$this, 'add'])
99 2
            ->addListener(EntityRemoved::ACTION_REMOVE, [$this, 'remove']);
100
101 2
        return $collection;
102
    }
103
104
    /**
105
     * Saves the relation row upon entity add
106
     *
107
     * @param EntityAdded $event
108
     */
109
    public function add(EntityAdded $event)
110
    {
111
        $entity = $event->getEntity();
112
        $collection = $event->getCollection();
113
        $this->deleteRelation($entity, $collection);
114
115
        $repository = $this->getParentRepository();
116
        $adapter = $repository->getAdapter();
117
        $value = $collection->parentEntity()->getId();
118
119
        Sql::createSql($adapter)
120
            ->insert($this->getRelationTable())
121
            ->set(
122
                [
123
                    "{$this->getRelatedForeignKey()}" => $entity->getId(),
124
                    "{$this->getForeignKey()}" => $value
125
                ]
126
            )
127
            ->execute();
128
    }
129
130
    /**
131
     * Deletes the relation row upon entity remove
132
     * 
133
     * @param EntityRemoved $event
134
     */
135
    public function remove(EntityRemoved $event)
136
    {
137
        $entity = $event->getEntity();
138
        $collection = $event->getCollection();
139
        $this->deleteRelation($entity, $collection);
140
    }
141
142
    /**
143
     * Removes existing relations
144
     *
145
     * @param EntityInterface $entity
146
     * @param EntityCollectionInterface $collection
147
     *
148
     * @return self
149
     */
150
    protected function deleteRelation(
151
        EntityInterface $entity,
152
        EntityCollectionInterface $collection
153
    ) {
154
        $repository = $this->getParentRepository();
155
        $adapter = $repository->getAdapter();
156
        $parts = explode('\\', $this->getEntityDescriptor()->className());
157
        $entityName = lcfirst(array_pop($parts));
158
        $parts = explode('\\', $this->getParentEntityDescriptor()->className());
159
        $relatedName = lcfirst(array_pop($parts));
160
        $value = $collection->parentEntity()->getId();
161
        Sql::createSql($adapter)
162
            ->delete($this->getRelationTable())
163
            ->where(
164
                [
0 ignored issues
show
Documentation introduced by
array("{$this->getRelate...ntityName}" => $value)) is of type array<string|integer,array<string|integer,*>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
165
                    "{$this->getRelatedForeignKey()} = :{$relatedName} AND
166
                     {$this->getForeignKey()} = :{$entityName}" => [
167
                        ":{$relatedName}" => $entity->getId(),
168
                        ":{$entityName}" => $value
169
                    ]
170
                ]
171
            )
172
            ->execute();
173
        return $this;
174
    }
175
176
    /**
177
     * Gets the relation conditions
178
     *
179
     * @param EntityInterface $entity
180
     * @return array
181
     */
182 2
    protected function getConditions(EntityInterface $entity)
183
    {
184 2
        $parts = explode('\\', $this->getEntityDescriptor()->className());
185 2
        $property = lcfirst(array_pop($parts));
186
        return [
187 2
            "rel.{$this->getForeignKey()} = :{$property}" => [
188 2
                ":{$property}" => $entity->getId()
189 2
            ]
190 2
        ];
191
    }
192
193
    /**
194
     * Adds the join table to the query
195
     *
196
     * @param Select $query
197
     * @return self
198
     */
199 2
    protected function addJoinTable(Select $query)
200
    {
201 2
        $relationTable = $this->getRelationTable();
202 2
        $table = $this->getParentTableName();
203 2
        $pmk = $this->getParentPrimaryKey();
204 2
        $rfk = $this->getRelatedForeignKey();
205 2
        $query->join(
206 2
            $relationTable,
207 2
            "rel.{$rfk} = {$table}.{$pmk}",
208 2
            null,
209
            'rel'
210 2
        );
211 2
        return $this;
212
    }
213
214
}