Many2Many::onParentSave()   C
last analyzed

Complexity

Conditions 10
Paths 10

Size

Total Lines 69
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 11
Bugs 3 Features 0
Metric Value
c 11
b 3
f 0
dl 0
loc 69
rs 6.0493
cc 10
eloc 45
nc 10
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 *
26
 * @package    Fwk
27
 * @subpackage Db
28
 * @subpackage Relations
29
 * @author     Julien Ballestracci <[email protected]>
30
 * @copyright  2011-2012 Julien Ballestracci <[email protected]>
31
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
32
 * @link       http://www.phpfwk.com
33
 */
34
namespace Fwk\Db\Relations;
35
36
use Fwk\Db\Events\AbstractEntityEvent;
37
use Fwk\Db\Events\AfterSaveEvent;
38
use Fwk\Db\Events\AfterUpdateEvent;
39
use Fwk\Db\RelationInterface;
40
use Fwk\Db\Query;
41
use Fwk\Db\Accessor;
42
use Fwk\Db\Registry\Registry;
43
use Fwk\Db\Registry\RegistryState;
44
use Fwk\Db\Workers\SaveEntityWorker;
45
use Fwk\Db\Workers\DeleteEntityWorker;
46
47
class Many2Many extends AbstractManyRelation implements RelationInterface
48
{
49
    protected $foreignTable;
50
51
    protected $foreignRefs;
52
53
    protected $foreignLink;
54
55
    public function __construct($local, $foreign, $table, $foreignTable,
56
        $foreignRefs, $foreignLink, $entity = null)
57
    {
58
        parent::__construct($local, $foreign, $table, $entity);
59
60
        $this->foreignTable = $foreignTable;
61
        $this->foreignRefs = $foreignRefs;
62
        $this->foreignLink = $foreignLink;
63
    }
64
65
    /**
66
     * Prepares a Query to fetch this relation (only FETCH_EAGER)
67
     *
68
     * @param  Query $query
69
     * @return void
70
     */
71
    public function prepare(Query $query, $columName)
72
    {
73
        if ($this->isLazy()) {
74
            return;
75
        }
76
77
        $join1 = array(
78
            'column' => 'skipped_join',
79
            'relation' => false,
80
            'skipped' => true,
81
            'reference' => null,
82
            'entity' => null
83
        );
84
85
        $this->columnName   = $columName;
86
        $join = array(
87
            'column' => $this->columnName,
88
            'relation' => $this,
89
            'skipped' => false,
90
            'reference' => $this->reference,
91
            'entity' => $this->entity
92
        );
93
94
        srand();
95
        $skAlias = 'skj' . rand(100, 999);
96
        $jAlias = 'j' . rand(100, 999);
97
98
        $query->join($this->foreignTable . ' ' . $skAlias, $this->local, $this->foreign, Query::JOIN_LEFT, $join1);
99
        $query->join($this->tableName . ' ' . $jAlias, $jAlias . '.' . $this->foreignRefs, $skAlias . '.' . $this->foreignLink, Query::JOIN_LEFT, $join);
100
    }
101
102
    /**
103
     *
104
     * @param mixed                  $object
105
     * @param \Fwk\Events\Dispatcher $evd
106
     */
107
    public function setParent($object, \Fwk\Events\Dispatcher $evd)
108
    {
109
        $return     = parent::setParent($object, $evd);
110
        if ($return === true) {
111
            $evd->on(AfterSaveEvent::EVENT_NAME, array($this, 'onParentSave'));
112
            $evd->on(AfterUpdateEvent::EVENT_NAME, array($this, 'onParentSave'));
113
        }
114
115
        return $return;
116
    }
117
118
    /**
119
     * Listener executed when parent entity is saved
120
     *
121
     * @param AbstractEntityEvent $event
122
     * @return void
123
     */
124
    public function  onParentSave(AbstractEntityEvent $event)
125
    {
126
        $connection     = $event->getConnection();
127
        $parentRegistry = $event->getTable()->getRegistry();
128
        $table          = $connection->table($this->getTableName());
129
        $registry       = $table->getRegistry();
130
131
        foreach ($this->getWorkersQueue() as $worker) {
132
            $worker->setRegistry($registry);
133
            $entity     = $worker->getEntity();
134
            $access     = new Accessor($this->parent);
135
136
            $entry      = $parentRegistry->getEntry($this->parent);
137
            $ids        = $entry->getIdentifiers();
138
139
            if (!count($ids)) {
140
                throw new \RuntimeException (sprintf('Parent (%s) have no identifiers defined', get_class($this->parent)));
141
            }
142
143
            if (!array_key_exists($this->local, $ids)) {
144
                throw new \RuntimeException (sprintf('Local key "%s" is not a valid identifier (primary key on %s)', $this->local, $registry->getTableName()));
145
            }
146
147
            if ($worker instanceof DeleteEntityWorker) {
148
                $value  = $access->get($this->local);
149
                if (empty($value)) {
150
                    throw new \RuntimeException (sprintf('Identifier not set on %s (column: %s)', get_class($this->parent), $this->local));
151
                }
152
153
                $params[]   = $value;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
154
                $acc        = Accessor::factory($entity)->get($this->local);
155
                $params[]   = $acc;
156
157
                if (empty($acc)) {
158
                    throw new \RuntimeException (sprintf('Identifier not set on %s (column: %s)', get_class($entity), $this->local));
159
                }
160
161
                $connection->execute(
162
                    Query::factory()
163
                        ->delete($this->foreignTable)
164
                        ->where(sprintf('%s = ?', $this->foreign))
165
                        ->andWhere(sprintf('%s = ?', $this->foreignLink)),
166
                    $params
167
                );
168
169
                $this->getRegistry()->remove($entity);
170
                continue;
171
            }
172
173
            if ($worker instanceof SaveEntityWorker) {
174
                $value  = $access->get($this->local);
175
                Accessor::factory($entity)->set($this->foreign, $value);
176
                $registry->markForAction($entity, Registry::ACTION_SAVE);
177
            }
178
179
            $worker->execute($connection);
180
181
            if ($worker instanceof SaveEntityWorker && $this->getRegistry()->getState($entity) == RegistryState::REGISTERED) {
182
                $connection->execute(
183
                    Query::factory()
184
                        ->insert($this->foreignTable)
185
                        ->set($this->foreign, '?')
186
                        ->set($this->foreignLink, '?'),
187
                    array($access->get($this->local), Accessor::factory($entity)->get($this->foreignRefs))
188
                );
189
                $this->getRegistry()->defineInitialValues($entity, $connection, $table);
190
            }
191
        }
192
    }
193
194
    public function fetch()
195
    {
196
        if (!$this->fetched && $this->isActive()) {
197
            $params     = array();
198
            $query      = new Query();
199
200
            $query->entity($this->entity);
201
            $query->select()
202
                    ->from($this->tableName, 'lazy')
203
                    ->where('1 = 1');
204
205
            if (isset($this->orderBy)) {
206
                $query->orderBy($this->orderBy['column'], $this->orderBy['direction']);
207
            }
208
209
             $join1 = array(
210
                'column'    => 'skipped_join',
211
                'skipped'   => true,
212
             );
213
214
            $query->join($this->getForeignTable() .' j', 'lazy.'. $this->getForeignReference(), $this->getForeignLink(), Query::JOIN_LEFT, $join1);
215
            $query->where('j.'. $this->foreign .'=?');
216
            $params[]   = $this->parentRefs;
217
218
            $connect    = $this->getConnection();
219
            $idKeys     = $connect->table($this->tableName)->getIdentifiersKeys();
220
221
            $res = $connect->execute($query, $params);
222
            foreach ($res as $result) {
223
                   $ids     = array();
224
                   $access  = new Accessor($result);
225
                   foreach ($idKeys as $key) {
226
                       $ids[$key]   = $access->get($key);
227
                   }
228
                   parent::add($result, $ids);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (add() instead of fetch()). Are you sure this is correct? If so, you might want to change this to $this->add().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
229
            }
230
231
            $this->setFetched(true);
232
        }
233
234
        return $this;
235
    }
236
237
    public function getForeignTable()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
238
    {
239
        return $this->foreignTable;
240
    }
241
242
    public function getForeignLink()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
243
    {
244
        return $this->foreignLink;
245
    }
246
247
    public function getForeignReference()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
248
    {
249
        return $this->foreignRefs;
250
    }
251
}