Extension::insertAsBulk()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 16
rs 9.9332
cc 2
nc 2
nop 2
1
<?php
2
3
/*
4
 * This file is part of the PHALCON-EXT package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace PhalconExt\Db;
13
14
use PhalconExt\Di\ProvidesDi;
15
16
/**
17
 * A cross platform extension to phalcon db adapter.
18
 *
19
 * @author  Jitendra Adhikari <[email protected]>
20
 * @license MIT
21
 *
22
 * @link    https://github.com/adhocore/phalcon-ext
23
 */
24
trait Extension
25
{
26
    use ProvidesDi;
27
28
    // Implemented by \Phalcon\Db\Adapter.
29
    abstract public function updateAsDict($table, $data, $conditions = null, $dataTypes = null);
30
31
    abstract public function insertAsDict($table, $data, $dataTypes = null);
32
33
    /**
34
     * Update a row matching given criteria if exists or insert new one.
35
     *
36
     * @param string $table    The table to act upon.
37
     * @param array  $data     The actual data dict ([field => value]) to update/insert.
38
     * @param array  $criteria The criteria dict ([field => value]) to match updatable row.
39
     *
40
     * @throws \InvalidArgumentException When the criteria is insufficient.
41
     *
42
     * @return bool
43
     */
44
    public function upsert(string $table, array $data, array $criteria): bool
45
    {
46
        if (empty($data)) {
47
            return false;
48
        }
49
50
        // Doesnt exist, insert new!
51
        if (0 === $count = $this->countBy($table, $criteria)) {
52
            return $this->insertAsDict($table, $data + $criteria);
53
        }
54
55
        // Ambiguous, multiple rows exist!
56
        if ($count > 1) {
57
            throw new \InvalidArgumentException('The criteria is not enough to fetch a single row for update!');
58
        }
59
60
        list($conditions, $bind) = $this->clauseBinds($criteria);
61
62
        // Update the existing data by criteria!
63
        return $this->updateAsDict($table, $data, \compact('conditions', 'bind'));
64
    }
65
66
    /**
67
     * Count rows in db table using given criteria.
68
     *
69
     * @param string $table
70
     * @param array  $criteria Col=>Val pairs
71
     *
72
     * @return int
73
     */
74
    public function countBy(string $table, array $criteria): int
75
    {
76
        if (empty($criteria)) {
77
            return $this->fetchColumn("SELECT COUNT(1) FROM {$table}");
78
        }
79
80
        list($clause, $binds) = $this->clauseBinds($criteria);
81
82
        return $this->fetchColumn("SELECT COUNT(1) FROM {$table} WHERE $clause", $binds) ?: 0;
83
    }
84
85
    /**
86
     * Prepare clause and Binds using data dict.
87
     *
88
     * @param array $dict  Col=>Val pairs
89
     * @param bool  $named Whether to use named placeholder.
90
     *
91
     * @return array ['clause', [binds]]
92
     */
93
    public function clauseBinds(array $dict, bool $named = false): array
94
    {
95
        $fields = [];
96
        foreach ($dict as $key => $value) {
97
            $fields[] = $named ? "$key = :$key" : "$key = ?";
98
        }
99
100
        return [
101
            \implode(' AND ', $fields),
102
            $named ? $dict : \array_values($dict),
103
        ];
104
    }
105
106
    /**
107
     * Insert bulk data to a table in single query.
108
     *
109
     * @param string $table
110
     * @param array  $data
111
     *
112
     * @return bool
113
     */
114
    public function insertAsBulk(string $table, array $data): bool
115
    {
116
        $binds   = [];
117
        $columns = $this->getInclusiveColumns($data);
118
        $default = \array_fill_keys($columns, null);
119
120
        foreach ($data as $row) {
121
            $row   = \array_merge($default, $row);
122
            $binds = \array_merge($binds, \array_values($row));
123
        }
124
125
        $sql  = "INSERT INTO {$table} (" . \implode(',', $columns) . ') VALUES ';
126
        $set  = '(' . \rtrim(\str_repeat('?,', \count($columns)), ',') . ')';
127
        $sql .= \rtrim(\str_repeat($set . ',', \count($data)), ',');
128
129
        return $this->execute($sql, $binds);
0 ignored issues
show
Bug introduced by
It seems like execute() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

129
        return $this->/** @scrutinizer ignore-call */ execute($sql, $binds);
Loading history...
130
    }
131
132
    /**
133
     * Get inclusive columns from multiple unbalanced/unorderd data dicts.
134
     *
135
     * @param array $data
136
     *
137
     * @return array
138
     */
139
    public function getInclusiveColumns(array $data): array
140
    {
141
        $columns = [];
142
143
        foreach (\array_filter($data, 'is_array') as $row) {
144
            $columns = \array_merge($columns, \array_keys($row));
145
        }
146
147
        $columns = \array_unique($columns);
148
        \sort($columns);
149
150
        return $columns;
151
    }
152
153
    /**
154
     * Register sql logger.
155
     *
156
     * @param array $config
157
     *
158
     * @return self
159
     */
160
    public function registerLogger(array $config): self
161
    {
162
        $evm = $this->di('eventsManager');
163
164
        $evm->attach('db', new Logger($config));
165
        $this->setEventsManager($evm);
0 ignored issues
show
Bug introduced by
It seems like setEventsManager() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

165
        $this->/** @scrutinizer ignore-call */ 
166
               setEventsManager($evm);
Loading history...
166
167
        return $this;
168
    }
169
}
170