SaveEntityWorker   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Dependencies 14

Importance

Changes 17
Bugs 7 Features 1
Metric Value
wmc 26
c 17
b 7
f 1
cbo 14
dl 0
loc 153
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
D execute() 0 143 26
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
 * @category   Database
27
 * @package    Fwk\Db
28
 * @subpackage Workers
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\Workers;
35
36
use Fwk\Db\Events\AfterSaveEvent;
37
use Fwk\Db\Events\AfterUpdateEvent;
38
use Fwk\Db\Events\BeforeSaveEvent;
39
use Fwk\Db\Events\BeforeUpdateEvent;
40
use Fwk\Db\Exceptions\UnregisteredEntityException;
41
use Fwk\Db\Query;
42
use Fwk\Db\Registry\RegistryState;
43
use Fwk\Db\WorkerInterface;
44
use Fwk\Db\Accessor;
45
use Fwk\Db\Connection;
46
47
/**
48
 * Save Entity WorkerInterface
49
 * 
50
 * This worker is used when an entity or relation have to be inserted or 
51
 * updated.
52
 * 
53
 * @category Workers
54
 * @package  Fwk\Db
55
 * @author   Julien Ballestracci <[email protected]>
56
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
57
 * @link     http://www.phpfwk.com
58
 */
59
class SaveEntityWorker extends AbstractWorker implements WorkerInterface
60
{
61
    /**
62
     * Executes the worker (SQL queries) and fire EntityEvents
63
     * 
64
     * @param Connection $connection Database connection
65
     * 
66
     * @return void
67
     */
68
    public function execute(Connection $connection)
69
    {
70
        $registry   = $this->getRegistry();
71
        $entry      = $registry->getEntry($this->entity);
72
        if (false === $entry) {
73
            throw new UnregisteredEntityException('Unregistered entity: '. get_class($this->entity));
74
        }
75
76
        $state      = $entry->getState();
77
        $table      = $connection->table($registry->getTableName());
78
        $query      = Query::factory();
79
        $queryParams = array();
80
        $access     = new Accessor($this->entity);
81
        $exec       = true;
82
        $tableRegistry = $connection->table($registry->getTableName())->getRegistry();
83
84
        if (in_array($this->entity, self::$working, true)) {
85
            return;
86
        }
87
88
        if ($tableRegistry !== $registry && $tableRegistry->contains($this->entity)) {
89
            $state = $tableRegistry->getState($this->entity);
90
        }
91
92
        switch ($state) {
93
        case RegistryState::UNKNOWN:
94
            throw new \LogicException(sprintf('Entity is in unknown state (%s)', get_class($this->entity)));
95
96
        case RegistryState::REGISTERED:
97
            array_push(self::$working, $this->entity);
98
            foreach ($access->getRelations() as $relation) {
99
                $relation->setParent(
100
                    $this->entity,
101
                    $this->getRegistry()->getEventDispatcher($this->entity)
102
                );
103
            }
104
105
            $registry->fireEvent(
106
                $this->entity,
107
                $event = new BeforeSaveEvent($connection, $table, $this->entity)
108
            );
109
110
            if ($event->isStopped()) {
111
                return;
112
            }
113
114
            $query->insert($table->getName());
115
            $values     = $access->toArray();
116
            $columns    = $table->getColumns();
117
            $setted     = 0;
118
119
            foreach ($columns as $columnObj) {
120
                $default = $columnObj->getDefault();
121
                $key = $columnObj->getName();
122
                $value  = (array_key_exists($key, $values) ? $values[$key] : -1);
123
124
                if (-1 === $value) {
125
                    if (!empty($default) || true === $columnObj->getAutoincrement()
126
                        || $columnObj->getNotnull() === true
127
                    ) {
128
                        continue;
129
                    }
130
                }
131
132
                $query->set($key, '?');
133
                $queryParams[] = $value;
134
                $setted++;
135
            }
136
137
            if (!$setted) {
138
                $exec = false;
139
            }
140
141
            $event = new AfterSaveEvent($connection, $table, $this->entity);
142
            break;
143
144
        case RegistryState::FRESH:
145
        case RegistryState::CHANGED:
146
            array_push(self::$working, $this->entity);
147
148
            $registry->fireEvent(
149
                $this->entity,
150
                new BeforeUpdateEvent($connection, $table, $this->entity)
151
            );
152
153
            $query->update($table->getName())->where('1 = 1');
154
155
            // reload changed values in case the event changed some...
156
            $changed    = $registry->getChangedValues($this->entity);
157
            $ids        = $entry->getIdentifiers();
158
            $idKeys     = $table->getIdentifiersKeys();
159
160
            if (!count($ids)) {
161
                static::removeFromWorking($this->entity);
162
                throw new \LogicException(
163
                    sprintf('Entity %s lacks identifiers and cannot be saved.', get_class($this->entity))
164
                );
165
            }
166
167
            $setted     = 0;
168
            foreach ($changed as $key => $value) {
169
                if ($table->hasColumn($key)) {
170
                    $query->set($key, '?');
171
                    $queryParams[] = $value;
172
                    $setted++;
173
                }
174
            }
175
176
            if (!$setted) {
177
                $exec   = false;
178
            }
179
180
            foreach ($idKeys as $key) {
181
                $query->andWhere(sprintf('`%s` = ?', $key));
182
                $value = $access->get($key);
183
                if (!$value) {
184
                    static::removeFromWorking($this->entity);
185
                    throw new \RuntimeException(
186
                        sprintf(
187
                            'Cannot save entity object (%s) because it lacks '.
188
                            'identifier (%s)',
189
                            get_class($this->entity),
190
                            $key
191
                        )
192
                    );
193
                }
194
195
                $queryParams[] = $value;
196
            }
197
198
            $event = new AfterUpdateEvent($connection, $table, $this->entity);
199
            break;
200
        }
201
202
        if ($exec) {
203
            $connection->execute($query, $queryParams);
204
        }
205
        $registry->defineInitialValues($this->entity);
206
        if (isset($event)) {
207
            $registry->fireEvent($this->entity, $event);
208
        }
209
        static::removeFromWorking($this->entity);
210
    }
211
}
212