22
33namespace Itosho \EasyQuery \Model \Behavior ;
44
5+ use Cake \Database \Expression \QueryExpression ;
6+ use Cake \Database \StatementInterface ;
7+ use Cake \I18n \FrozenTime ;
58use Cake \ORM \Behavior ;
9+ use Cake \ORM \Entity ;
10+ use Cake \ORM \Query ;
11+ use Cake \ORM \TableRegistry ;
612use LogicException ;
713
814/**
@@ -22,9 +28,9 @@ class InsertBehavior extends Behavior
2228 /**
2329 * execute bulk insert query
2430 *
25- * @param \Cake\ORM\ Entity[] $entities insert entities
31+ * @param Entity[] $entities insert entities
2632 * @throws LogicException no save data
27- * @return \Cake\Database\ StatementInterface query result
33+ * @return StatementInterface query result
2834 */
2935 public function bulkInsert (array $ entities )
3036 {
@@ -49,4 +55,113 @@ public function bulkInsert(array $entities)
4955
5056 return $ query ->execute ();
5157 }
58+
59+ /**
60+ * execute insert select query for saving a record just once
61+ *
62+ * @param Entity $entity insert entity
63+ * @param array|null $conditions search conditions
64+ * @return StatementInterface query result
65+ */
66+ public function insertOnce (Entity $ entity , array $ conditions = null )
67+ {
68+ if ($ this ->_config ['event ' ]['beforeSave ' ]) {
69+ $ this ->_table ->dispatchEvent ('Model.beforeSave ' , compact ('entity ' ));
70+ }
71+
72+ $ entity ->setVirtual ([]);
73+ $ insertData = $ entity ->toArray ();
74+ if (isset ($ insertData ['created ' ]) && !is_null ($ insertData ['created ' ])) {
75+ $ insertData ['created ' ] = FrozenTime::now ()->toDateTimeString ();
76+ }
77+ if (isset ($ insertData ['modified ' ]) && !is_null ($ insertData ['modified ' ])) {
78+ $ insertData ['modified ' ] = FrozenTime::now ()->toDateTimeString ();
79+ }
80+
81+ $ escape = function ($ content ) {
82+ return is_null ($ content ) ? 'NULL ' : '\'' . addslashes ($ content ) . '\'' ;
83+ };
84+
85+ $ escapedInsertData = array_map ($ escape , $ insertData );
86+ $ fields = array_keys ($ insertData );
87+ $ existsConditions = $ conditions ;
88+ if (is_null ($ existsConditions )) {
89+ $ existsConditions = $ this ->getExistsConditions ($ escapedInsertData );
90+ }
91+
92+ $ query = $ this ->_table
93+ ->query ()
94+ ->insert ($ fields )
95+ ->epilog (
96+ $ this
97+ ->buildTmpTableSelectQuery ($ escapedInsertData )
98+ ->where (function (QueryExpression $ exp ) use ($ existsConditions ) {
99+ $ query = $ this ->_table
100+ ->find ()
101+ ->where ($ existsConditions );
102+
103+ return $ exp ->notExists ($ query );
104+ })
105+ ->limit (1 )
106+ );
107+
108+ return $ query ->execute ();
109+ }
110+
111+ /**
112+ * build tmp table's select query for insert select query
113+ *
114+ * @param array $escapedData escaped array data
115+ * @throws LogicException select query is invalid
116+ * @return Query tmp table's select query
117+ */
118+ private function buildTmpTableSelectQuery ($ escapedData )
119+ {
120+ $ driver = $ this ->_table
121+ ->getConnection ()
122+ ->getDriver ();
123+ $ schema = [];
124+ foreach ($ escapedData as $ key => $ value ) {
125+ $ col = $ driver ->quoteIdentifier ($ key );
126+ $ schema [] = "{$ value } AS {$ col }" ;
127+ }
128+
129+ $ tmpTable = TableRegistry::getTableLocator ()->get ('tmp ' , [
130+ 'schema ' => $ this ->_table ->getSchema ()
131+ ]);
132+ $ query = $ tmpTable
133+ ->find ()
134+ ->select (array_keys ($ escapedData ))
135+ ->from (
136+ sprintf ('(SELECT %s) as tmp ' , implode (', ' , $ schema ))
137+ );
138+ /** @var Query $selectQuery */
139+ $ selectQuery = $ query ;
140+
141+ return $ selectQuery ;
142+ }
143+
144+ /**
145+ * get conditions for finding a record already exists
146+ *
147+ * @param array $escapedData escaped array data
148+ * @return array conditions
149+ */
150+ private function getExistsConditions ($ escapedData )
151+ {
152+ $ autoFillFields = ['created ' , 'modified ' ];
153+ $ existsConditions = [];
154+ foreach ($ escapedData as $ field => $ value ) {
155+ if (in_array ($ field , $ autoFillFields , true )) {
156+ continue ;
157+ }
158+ if ($ value === 'NULL ' ) {
159+ $ existsConditions [] = "{$ field } IS NULL " ;
160+ } else {
161+ $ existsConditions [] = "{$ field } = {$ value }" ;
162+ }
163+ }
164+
165+ return $ existsConditions ;
166+ }
52167}
0 commit comments