Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Insert
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
6 / 6
15
100.00% covered (success)
100.00%
1 / 1
 into
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 values
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 onConflict
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 onDuplicateKeyUpdate
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
10
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Db\Sql;
15
16/**
17 * Insert class
18 *
19 * @category   Pop
20 * @package    Pop\Db
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    6.5.0
25 */
26class Insert extends AbstractClause
27{
28
29    /**
30     * Conflict key for UPSERT
31     * @var ?string
32     */
33    protected ?string $conflictKey = null;
34
35    /**
36     * Conflict columns for UPSERT
37     * @var array
38     */
39    protected array $conflictColumns = [];
40
41    /**
42     * Set into table
43     *
44     * @param  mixed $table
45     * @return Insert
46     */
47    public function into(mixed $table): Insert
48    {
49        $this->setTable($table);
50        return $this;
51    }
52
53    /**
54     * Set a value
55     *
56     * @param  array $values
57     * @return Insert
58     */
59    public function values(array $values): Insert
60    {
61        $this->setValues($values);
62        return $this;
63    }
64
65    /**
66     * Set what to do on a insert conflict (UPSERT - PostgreSQL & SQLite)
67     *
68     * @param  array   $columns
69     * @param  ?string $key
70     * @return Insert
71     */
72    public function onConflict(array $columns, ?string $key = null): Insert
73    {
74        $this->conflictColumns = $columns;
75        $this->conflictKey     = $key;
76        return $this;
77    }
78
79    /**
80     * Set columns to handle duplicates/conflicts (UPSERT - MySQL-ism)
81     *
82     * @param  array $columns
83     * @return Insert
84     */
85    public function onDuplicateKeyUpdate(array $columns): Insert
86    {
87        $this->onConflict($columns);
88        return $this;
89    }
90
91    /**
92     * Render the INSERT statement
93     *
94     * @return string
95     */
96    public function render(): string
97    {
98        // Start building the INSERT statement
99        $sql     = 'INSERT INTO ' . $this->quoteId($this->table) . ' ';
100        $columns = [];
101        $values  = [];
102
103        $paramCount = 1;
104        $dbType     = $this->getDbType();
105
106        foreach ($this->values as $column => $value) {
107            $colValue = (strpos($column, '.') !== false) ?
108                substr($column, (strpos($column, '.') + 1)) : $column;
109
110            $columns[] = $this->quoteId($column);
111            $values[]  = ($value === null) ? 'NULL' : $this->quote($value);
112        }
113
114        $sql .= '(' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
115
116        // Handle conflicts/duplicates (UPSERT)
117        if (!empty($this->conflictColumns)) {
118            $updates = [];
119            switch ($dbType) {
120                case self::MYSQL:
121                    foreach ($this->conflictColumns as $conflictColumn) {
122                        $updates[] = $this->quoteId($conflictColumn) . ' = VALUES(' . $conflictColumn .')';
123                    }
124                    $sql .= ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
125                    break;
126                case self::SQLITE:
127                case self::PGSQL:
128                    foreach ($this->conflictColumns as $conflictColumn) {
129                        $updates[] = $this->quoteId($conflictColumn) . ' = excluded.' . $conflictColumn;
130                    }
131                    $sql .= ' ON CONFLICT (' . $this->quoteId($this->conflictKey) . ') DO UPDATE SET '
132                        . implode(', ', $updates);
133                    break;
134            }
135        }
136
137        return $sql;
138    }
139
140    /**
141     * Render the INSERT statement
142     *
143     * @return string
144     */
145    public function __toString(): string
146    {
147        return $this->render();
148    }
149
150}