正在显示
13 个修改的文件
包含
3855 行增加
和
2 行删除
@@ -4,7 +4,7 @@ use think\Db; | @@ -4,7 +4,7 @@ use think\Db; | ||
4 | // 数据库配置信息设置(全局有效) | 4 | // 数据库配置信息设置(全局有效) |
5 | 5 | ||
6 | require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; | 6 | require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; |
7 | - | 7 | +require_once __DIR__ . '/simplewind/vendor/autoload.php'; |
8 | //$global_uid = 0; | 8 | //$global_uid = 0; |
9 | // | 9 | // |
10 | //// 当客户端连上来时分配uid,并保存连接,并通知所有客户端 | 10 | //// 当客户端连上来时分配uid,并保存连接,并通知所有客户端 |
@@ -124,7 +124,7 @@ require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; | @@ -124,7 +124,7 @@ require_once __DIR__ . '/simplewind/vendor/workerman/workerman/Autoloader.php'; | ||
124 | 124 | ||
125 | // 创建一个Worker监听2347端口,不使用任何应用层协议 | 125 | // 创建一个Worker监听2347端口,不使用任何应用层协议 |
126 | $tcp_worker = new Worker("websocket://114.215.223.17:5001"); | 126 | $tcp_worker = new Worker("websocket://114.215.223.17:5001"); |
127 | -//Db::setConfig(['数据库配置参数(数组)']); | 127 | + |
128 | //$tcp_worker->name='panhaowen'; | 128 | //$tcp_worker->name='panhaowen'; |
129 | // 启动4个进程对外提供服务 | 129 | // 启动4个进程对外提供服务 |
130 | $tcp_worker->count = 1; | 130 | $tcp_worker->count = 1; |
1 | +.idea |
simplewind/vendor/topthink/think-orm/LICENSE
0 → 100644
1 | + Apache License | ||
2 | + Version 2.0, January 2004 | ||
3 | + http://www.apache.org/licenses/ | ||
4 | + | ||
5 | + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
6 | + | ||
7 | + 1. Definitions. | ||
8 | + | ||
9 | + "License" shall mean the terms and conditions for use, reproduction, | ||
10 | + and distribution as defined by Sections 1 through 9 of this document. | ||
11 | + | ||
12 | + "Licensor" shall mean the copyright owner or entity authorized by | ||
13 | + the copyright owner that is granting the License. | ||
14 | + | ||
15 | + "Legal Entity" shall mean the union of the acting entity and all | ||
16 | + other entities that control, are controlled by, or are under common | ||
17 | + control with that entity. For the purposes of this definition, | ||
18 | + "control" means (i) the power, direct or indirect, to cause the | ||
19 | + direction or management of such entity, whether by contract or | ||
20 | + otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
21 | + outstanding shares, or (iii) beneficial ownership of such entity. | ||
22 | + | ||
23 | + "You" (or "Your") shall mean an individual or Legal Entity | ||
24 | + exercising permissions granted by this License. | ||
25 | + | ||
26 | + "Source" form shall mean the preferred form for making modifications, | ||
27 | + including but not limited to software source code, documentation | ||
28 | + source, and configuration files. | ||
29 | + | ||
30 | + "Object" form shall mean any form resulting from mechanical | ||
31 | + transformation or translation of a Source form, including but | ||
32 | + not limited to compiled object code, generated documentation, | ||
33 | + and conversions to other media types. | ||
34 | + | ||
35 | + "Work" shall mean the work of authorship, whether in Source or | ||
36 | + Object form, made available under the License, as indicated by a | ||
37 | + copyright notice that is included in or attached to the work | ||
38 | + (an example is provided in the Appendix below). | ||
39 | + | ||
40 | + "Derivative Works" shall mean any work, whether in Source or Object | ||
41 | + form, that is based on (or derived from) the Work and for which the | ||
42 | + editorial revisions, annotations, elaborations, or other modifications | ||
43 | + represent, as a whole, an original work of authorship. For the purposes | ||
44 | + of this License, Derivative Works shall not include works that remain | ||
45 | + separable from, or merely link (or bind by name) to the interfaces of, | ||
46 | + the Work and Derivative Works thereof. | ||
47 | + | ||
48 | + "Contribution" shall mean any work of authorship, including | ||
49 | + the original version of the Work and any modifications or additions | ||
50 | + to that Work or Derivative Works thereof, that is intentionally | ||
51 | + submitted to Licensor for inclusion in the Work by the copyright owner | ||
52 | + or by an individual or Legal Entity authorized to submit on behalf of | ||
53 | + the copyright owner. For the purposes of this definition, "submitted" | ||
54 | + means any form of electronic, verbal, or written communication sent | ||
55 | + to the Licensor or its representatives, including but not limited to | ||
56 | + communication on electronic mailing lists, source code control systems, | ||
57 | + and issue tracking systems that are managed by, or on behalf of, the | ||
58 | + Licensor for the purpose of discussing and improving the Work, but | ||
59 | + excluding communication that is conspicuously marked or otherwise | ||
60 | + designated in writing by the copyright owner as "Not a Contribution." | ||
61 | + | ||
62 | + "Contributor" shall mean Licensor and any individual or Legal Entity | ||
63 | + on behalf of whom a Contribution has been received by Licensor and | ||
64 | + subsequently incorporated within the Work. | ||
65 | + | ||
66 | + 2. Grant of Copyright License. Subject to the terms and conditions of | ||
67 | + this License, each Contributor hereby grants to You a perpetual, | ||
68 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
69 | + copyright license to reproduce, prepare Derivative Works of, | ||
70 | + publicly display, publicly perform, sublicense, and distribute the | ||
71 | + Work and such Derivative Works in Source or Object form. | ||
72 | + | ||
73 | + 3. Grant of Patent License. Subject to the terms and conditions of | ||
74 | + this License, each Contributor hereby grants to You a perpetual, | ||
75 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
76 | + (except as stated in this section) patent license to make, have made, | ||
77 | + use, offer to sell, sell, import, and otherwise transfer the Work, | ||
78 | + where such license applies only to those patent claims licensable | ||
79 | + by such Contributor that are necessarily infringed by their | ||
80 | + Contribution(s) alone or by combination of their Contribution(s) | ||
81 | + with the Work to which such Contribution(s) was submitted. If You | ||
82 | + institute patent litigation against any entity (including a | ||
83 | + cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
84 | + or a Contribution incorporated within the Work constitutes direct | ||
85 | + or contributory patent infringement, then any patent licenses | ||
86 | + granted to You under this License for that Work shall terminate | ||
87 | + as of the date such litigation is filed. | ||
88 | + | ||
89 | + 4. Redistribution. You may reproduce and distribute copies of the | ||
90 | + Work or Derivative Works thereof in any medium, with or without | ||
91 | + modifications, and in Source or Object form, provided that You | ||
92 | + meet the following conditions: | ||
93 | + | ||
94 | + (a) You must give any other recipients of the Work or | ||
95 | + Derivative Works a copy of this License; and | ||
96 | + | ||
97 | + (b) You must cause any modified files to carry prominent notices | ||
98 | + stating that You changed the files; and | ||
99 | + | ||
100 | + (c) You must retain, in the Source form of any Derivative Works | ||
101 | + that You distribute, all copyright, patent, trademark, and | ||
102 | + attribution notices from the Source form of the Work, | ||
103 | + excluding those notices that do not pertain to any part of | ||
104 | + the Derivative Works; and | ||
105 | + | ||
106 | + (d) If the Work includes a "NOTICE" text file as part of its | ||
107 | + distribution, then any Derivative Works that You distribute must | ||
108 | + include a readable copy of the attribution notices contained | ||
109 | + within such NOTICE file, excluding those notices that do not | ||
110 | + pertain to any part of the Derivative Works, in at least one | ||
111 | + of the following places: within a NOTICE text file distributed | ||
112 | + as part of the Derivative Works; within the Source form or | ||
113 | + documentation, if provided along with the Derivative Works; or, | ||
114 | + within a display generated by the Derivative Works, if and | ||
115 | + wherever such third-party notices normally appear. The contents | ||
116 | + of the NOTICE file are for informational purposes only and | ||
117 | + do not modify the License. You may add Your own attribution | ||
118 | + notices within Derivative Works that You distribute, alongside | ||
119 | + or as an addendum to the NOTICE text from the Work, provided | ||
120 | + that such additional attribution notices cannot be construed | ||
121 | + as modifying the License. | ||
122 | + | ||
123 | + You may add Your own copyright statement to Your modifications and | ||
124 | + may provide additional or different license terms and conditions | ||
125 | + for use, reproduction, or distribution of Your modifications, or | ||
126 | + for any such Derivative Works as a whole, provided Your use, | ||
127 | + reproduction, and distribution of the Work otherwise complies with | ||
128 | + the conditions stated in this License. | ||
129 | + | ||
130 | + 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
131 | + any Contribution intentionally submitted for inclusion in the Work | ||
132 | + by You to the Licensor shall be under the terms and conditions of | ||
133 | + this License, without any additional terms or conditions. | ||
134 | + Notwithstanding the above, nothing herein shall supersede or modify | ||
135 | + the terms of any separate license agreement you may have executed | ||
136 | + with Licensor regarding such Contributions. | ||
137 | + | ||
138 | + 6. Trademarks. This License does not grant permission to use the trade | ||
139 | + names, trademarks, service marks, or product names of the Licensor, | ||
140 | + except as required for reasonable and customary use in describing the | ||
141 | + origin of the Work and reproducing the content of the NOTICE file. | ||
142 | + | ||
143 | + 7. Disclaimer of Warranty. Unless required by applicable law or | ||
144 | + agreed to in writing, Licensor provides the Work (and each | ||
145 | + Contributor provides its Contributions) on an "AS IS" BASIS, | ||
146 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
147 | + implied, including, without limitation, any warranties or conditions | ||
148 | + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
149 | + PARTICULAR PURPOSE. You are solely responsible for determining the | ||
150 | + appropriateness of using or redistributing the Work and assume any | ||
151 | + risks associated with Your exercise of permissions under this License. | ||
152 | + | ||
153 | + 8. Limitation of Liability. In no event and under no legal theory, | ||
154 | + whether in tort (including negligence), contract, or otherwise, | ||
155 | + unless required by applicable law (such as deliberate and grossly | ||
156 | + negligent acts) or agreed to in writing, shall any Contributor be | ||
157 | + liable to You for damages, including any direct, indirect, special, | ||
158 | + incidental, or consequential damages of any character arising as a | ||
159 | + result of this License or out of the use or inability to use the | ||
160 | + Work (including but not limited to damages for loss of goodwill, | ||
161 | + work stoppage, computer failure or malfunction, or any and all | ||
162 | + other commercial damages or losses), even if such Contributor | ||
163 | + has been advised of the possibility of such damages. | ||
164 | + | ||
165 | + 9. Accepting Warranty or Additional Liability. While redistributing | ||
166 | + the Work or Derivative Works thereof, You may choose to offer, | ||
167 | + and charge a fee for, acceptance of support, warranty, indemnity, | ||
168 | + or other liability obligations and/or rights consistent with this | ||
169 | + License. However, in accepting such obligations, You may act only | ||
170 | + on Your own behalf and on Your sole responsibility, not on behalf | ||
171 | + of any other Contributor, and only if You agree to indemnify, | ||
172 | + defend, and hold each Contributor harmless for any liability | ||
173 | + incurred by, or claims asserted against, such Contributor by reason | ||
174 | + of your accepting any such warranty or additional liability. | ||
175 | + | ||
176 | + END OF TERMS AND CONDITIONS | ||
177 | + | ||
178 | + APPENDIX: How to apply the Apache License to your work. | ||
179 | + | ||
180 | + To apply the Apache License to your work, attach the following | ||
181 | + boilerplate notice, with the fields enclosed by brackets "{}" | ||
182 | + replaced with your own identifying information. (Don't include | ||
183 | + the brackets!) The text should be enclosed in the appropriate | ||
184 | + comment syntax for the file format. We also recommend that a | ||
185 | + file or class name and description of purpose be included on the | ||
186 | + same "printed page" as the copyright notice for easier | ||
187 | + identification within third-party archives. | ||
188 | + | ||
189 | + Copyright {yyyy} {name of copyright owner} | ||
190 | + | ||
191 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
192 | + you may not use this file except in compliance with the License. | ||
193 | + You may obtain a copy of the License at | ||
194 | + | ||
195 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
196 | + | ||
197 | + Unless required by applicable law or agreed to in writing, software | ||
198 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
199 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
200 | + See the License for the specific language governing permissions and | ||
201 | + limitations under the License. |
1 | +# think-orm | ||
2 | + | ||
3 | +基于PHP5.6+ 的ORM实现,主要特性: | ||
4 | + | ||
5 | +- 基于ThinkPHP5.1的ORM独立封装 | ||
6 | +- 支持Mysql、Pgsql、Sqlite、SqlServer、Oracle和Mongodb | ||
7 | +- 支持Db类和查询构造器 | ||
8 | +- 支持事务 | ||
9 | +- 支持模型和关联 | ||
10 | + | ||
11 | +适用于不使用ThinkPHP框架的开发者。 | ||
12 | + | ||
13 | +安装 | ||
14 | +~~~ | ||
15 | +composer require topthink/think-orm | ||
16 | +~~~ | ||
17 | + | ||
18 | +Db类用法: | ||
19 | +~~~php | ||
20 | +use think\Db; | ||
21 | +// 数据库配置信息设置(全局有效) | ||
22 | +Db::setConfig(['数据库配置参数(数组)']); | ||
23 | +// 进行CURD操作 | ||
24 | +Db::table('user') | ||
25 | + ->data(['name'=>'thinkphp','email'=>'thinkphp@qq.com']) | ||
26 | + ->insert(); | ||
27 | +Db::table('user')->find(); | ||
28 | +Db::table('user') | ||
29 | + ->where('id','>',10) | ||
30 | + ->order('id','desc') | ||
31 | + ->limit(10) | ||
32 | + ->select(); | ||
33 | +Db::table('user') | ||
34 | + ->where('id',10) | ||
35 | + ->update(['name'=>'test']); | ||
36 | +Db::table('user') | ||
37 | + ->where('id',10) | ||
38 | + ->delete(); | ||
39 | +~~~ | ||
40 | + | ||
41 | +Db类增加的(静态)方法包括: | ||
42 | +- `setConfig` 设置全局配置信息 | ||
43 | +- `getConfig` 获取数据库配置信息 | ||
44 | +- `setQuery` 设置数据库Query类名称 | ||
45 | +- `setCacheHandler` 设置缓存对象Handler(必须支持get、set及rm方法) | ||
46 | +- `getSqlLog` 用于获取当前请求的SQL日志信息(包含连接信息) | ||
47 | + | ||
48 | +其它操作参考TP5.1的完全开发手册[数据库](https://www.kancloud.cn/manual/thinkphp5_1/353998)章节 | ||
49 | + | ||
50 | +定义模型: | ||
51 | +~~~php | ||
52 | +namespace app\index\model; | ||
53 | +use think\Model; | ||
54 | +class User extends Model | ||
55 | +{ | ||
56 | +} | ||
57 | +~~~ | ||
58 | + | ||
59 | +代码调用: | ||
60 | + | ||
61 | +~~~php | ||
62 | +use app\index\model\User; | ||
63 | + | ||
64 | +$user = User::get(1); | ||
65 | +$user->name = 'thinkphp'; | ||
66 | +$user->save(); | ||
67 | +~~~ | ||
68 | + | ||
69 | +## Db类和模型对比使用 | ||
70 | +#### :white_check_mark: 创建Create | ||
71 | +* Db用法 | ||
72 | + | ||
73 | + ```php | ||
74 | + Db::table('user') | ||
75 | + ->insert([ | ||
76 | + 'name' => 'thinkphp', | ||
77 | + 'email' => 'thinkphp@qq.com', | ||
78 | + ]); | ||
79 | + ``` | ||
80 | +* 模型用法 | ||
81 | + | ||
82 | + ```php | ||
83 | + $user = new User; | ||
84 | + $user->name = 'thinkphp'; | ||
85 | + $user->email = 'thinkphp@qq.com'; | ||
86 | + $user->save(); | ||
87 | + ``` | ||
88 | +* 或者批量设置 | ||
89 | + | ||
90 | + ```php | ||
91 | + $user = new User; | ||
92 | + $user->save([ | ||
93 | + 'name' => 'thinkphp', | ||
94 | + 'email' => 'thinkphp@qq.com', | ||
95 | + ]); | ||
96 | + ``` | ||
97 | +#### :white_check_mark: 读取Read | ||
98 | +* Db用法 | ||
99 | + | ||
100 | + ```php | ||
101 | + $user = Db::table('user') | ||
102 | + ->where('id', 1) | ||
103 | + ->find(); | ||
104 | + // 或者 | ||
105 | + $user = Db::table('user') | ||
106 | + ->find(1); | ||
107 | + echo $user['id']; | ||
108 | + echo $user['name']; | ||
109 | + ``` | ||
110 | +* 模型用法 | ||
111 | + | ||
112 | + ```php | ||
113 | + $user = User::get(1); | ||
114 | + echo $user->id; | ||
115 | + echo $user->name; | ||
116 | + ``` | ||
117 | +* 模型实现读取多个记录 | ||
118 | + | ||
119 | + ```php | ||
120 | + // 查询用户数据集 | ||
121 | + $users = User::where('id', '>', 1) | ||
122 | + ->limit(5) | ||
123 | + ->select(); | ||
124 | + | ||
125 | + // 遍历读取用户数据 | ||
126 | + foreach ($users as $user) { | ||
127 | + echo $user->id; | ||
128 | + echo $user->name; | ||
129 | + } | ||
130 | + ``` | ||
131 | +#### :white_check_mark: 更新Update | ||
132 | +* Db用法 | ||
133 | + | ||
134 | + ```php | ||
135 | + Db::table('user') | ||
136 | + ->where('id', 1) | ||
137 | + ->update([ | ||
138 | + 'name' => 'topthink', | ||
139 | + 'email' => 'topthink@qq.com', | ||
140 | + ]); | ||
141 | + ``` | ||
142 | +* 模型用法 | ||
143 | + | ||
144 | + ```php | ||
145 | + $user = User::get(1); | ||
146 | + $user->name = 'topthink'; | ||
147 | + $user->email = 'topthink@qq.com'; | ||
148 | + $user->save(); | ||
149 | + ``` | ||
150 | +* 或者使用 | ||
151 | + | ||
152 | + ```php | ||
153 | + $user = User::get(1); | ||
154 | + $user->save([ | ||
155 | + 'name' => 'topthink', | ||
156 | + 'email' => 'topthink@qq.com', | ||
157 | + ]); | ||
158 | + ``` | ||
159 | +* 静态调用 | ||
160 | + | ||
161 | + ```php | ||
162 | + User::update([ | ||
163 | + 'name' => 'topthink', | ||
164 | + 'email' => 'topthink@qq.com', | ||
165 | + ], ['id' => 1]); | ||
166 | + ``` | ||
167 | +#### :white_check_mark: 删除Delete | ||
168 | +* Db用法 | ||
169 | + | ||
170 | + ```php | ||
171 | + Db::table('user')->delete(1); | ||
172 | + ``` | ||
173 | +* 模型用法 | ||
174 | + | ||
175 | + ```php | ||
176 | + $user = User::get(1); | ||
177 | + $user->delete(); | ||
178 | + ``` | ||
179 | +* 或者静态实现 | ||
180 | + | ||
181 | + ```php | ||
182 | + User::destroy(1); | ||
183 | + ``` | ||
184 | +* 静态调用 | ||
185 | + | ||
186 | + ```php | ||
187 | + User::update([ | ||
188 | + 'name' => 'topthink', | ||
189 | + 'email' => 'topthink@qq.com', | ||
190 | + ], ['id' => 1]); | ||
191 | + ``` | ||
192 | +* destroy方法支持删除指定主键或者查询条件的数据 | ||
193 | + | ||
194 | + ```php | ||
195 | + // 根据主键删除多个数据 | ||
196 | + User::destroy([1, 2, 3]); | ||
197 | + // 指定条件删除数据 | ||
198 | + User::destroy([ | ||
199 | + 'status' => 0, | ||
200 | + ]); | ||
201 | + // 使用闭包条件 | ||
202 | + User::destroy(function ($query) { | ||
203 | + $query->where('id', '>', 0) | ||
204 | + ->where('status', 0); | ||
205 | + }); | ||
206 | + ``` | ||
207 | +更多模型用法可以参考5.1完全开发手册的[模型](https://www.kancloud.cn/manual/thinkphp5_1/354041)章节 |
1 | +{ | ||
2 | + "name": "topthink/think-orm", | ||
3 | + "description": "think orm", | ||
4 | + "license": "Apache-2.0", | ||
5 | + "authors": [ | ||
6 | + { | ||
7 | + "name": "liu21st", | ||
8 | + "email": "liu21st@gmail.com" | ||
9 | + } | ||
10 | + ], | ||
11 | + "require": { | ||
12 | + "php": ">=5.6.0" | ||
13 | + }, | ||
14 | + "autoload": { | ||
15 | + "psr-4": { | ||
16 | + "think\\": "src" | ||
17 | + }, | ||
18 | + "files": [ | ||
19 | + "src/config.php" | ||
20 | + ] | ||
21 | + } | ||
22 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace think; | ||
4 | + | ||
5 | +/** | ||
6 | + * 数据库缓存接口 | ||
7 | + * Interface CacheInterface | ||
8 | + * @author : evalor <master@evalor.cn> | ||
9 | + * @package think | ||
10 | + */ | ||
11 | +interface CacheInterface | ||
12 | +{ | ||
13 | + function get($name, $default = false); | ||
14 | + | ||
15 | + function set($name, $value, $expire = null); | ||
16 | + | ||
17 | + function rm($name); | ||
18 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: zhangyajun <448901948@qq.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think; | ||
13 | + | ||
14 | +use ArrayAccess; | ||
15 | +use ArrayIterator; | ||
16 | +use Countable; | ||
17 | +use IteratorAggregate; | ||
18 | +use JsonSerializable; | ||
19 | + | ||
20 | +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable | ||
21 | +{ | ||
22 | + protected $items = []; | ||
23 | + | ||
24 | + public function __construct($items = []) | ||
25 | + { | ||
26 | + $this->items = $this->convertToArray($items); | ||
27 | + } | ||
28 | + | ||
29 | + public static function make($items = []) | ||
30 | + { | ||
31 | + return new static($items); | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * 是否为空 | ||
36 | + * @return bool | ||
37 | + */ | ||
38 | + public function isEmpty() | ||
39 | + { | ||
40 | + return empty($this->items); | ||
41 | + } | ||
42 | + | ||
43 | + public function toArray() | ||
44 | + { | ||
45 | + return array_map(function ($value) { | ||
46 | + return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; | ||
47 | + }, $this->items); | ||
48 | + } | ||
49 | + | ||
50 | + public function all() | ||
51 | + { | ||
52 | + return $this->items; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * 合并数组 | ||
57 | + * | ||
58 | + * @param mixed $items | ||
59 | + * @return static | ||
60 | + */ | ||
61 | + public function merge($items) | ||
62 | + { | ||
63 | + return new static(array_merge($this->items, $this->convertToArray($items))); | ||
64 | + } | ||
65 | + | ||
66 | + /** | ||
67 | + * 交换数组中的键和值 | ||
68 | + * | ||
69 | + * @return static | ||
70 | + */ | ||
71 | + public function flip() | ||
72 | + { | ||
73 | + return new static(array_flip($this->items)); | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * 按指定键整理数据 | ||
78 | + * | ||
79 | + * @access public | ||
80 | + * @param mixed $items 数据 | ||
81 | + * @param string $indexKey 键名 | ||
82 | + * @return array | ||
83 | + */ | ||
84 | + public function dictionary($items = null, &$indexKey = null) | ||
85 | + { | ||
86 | + if ($items instanceof self || $items instanceof Paginator) { | ||
87 | + $items = $items->all(); | ||
88 | + } | ||
89 | + | ||
90 | + $items = is_null($items) ? $this->items : $items; | ||
91 | + | ||
92 | + if ($items && empty($indexKey)) { | ||
93 | + $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk(); | ||
94 | + } | ||
95 | + | ||
96 | + if (isset($indexKey) && is_string($indexKey)) { | ||
97 | + return array_column($items, null, $indexKey); | ||
98 | + } | ||
99 | + | ||
100 | + return $items; | ||
101 | + } | ||
102 | + | ||
103 | + /** | ||
104 | + * 比较数组,返回差集 | ||
105 | + * | ||
106 | + * @access public | ||
107 | + * @param mixed $items 数据 | ||
108 | + * @param string $indexKey 指定比较的键名 | ||
109 | + * @return static | ||
110 | + */ | ||
111 | + public function diff($items, $indexKey = null) | ||
112 | + { | ||
113 | + if ($this->isEmpty() || is_scalar($this->items[0])) { | ||
114 | + return new static(array_diff($this->items, $this->convertToArray($items))); | ||
115 | + } | ||
116 | + | ||
117 | + $diff = []; | ||
118 | + $dictionary = $this->dictionary($items, $indexKey); | ||
119 | + | ||
120 | + if (is_string($indexKey)) { | ||
121 | + foreach ($this->items as $item) { | ||
122 | + if (!isset($dictionary[$item[$indexKey]])) { | ||
123 | + $diff[] = $item; | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + return new static($diff); | ||
129 | + } | ||
130 | + | ||
131 | + /** | ||
132 | + * 比较数组,返回交集 | ||
133 | + * | ||
134 | + * @access public | ||
135 | + * @param mixed $items 数据 | ||
136 | + * @param string $indexKey 指定比较的键名 | ||
137 | + * @return static | ||
138 | + */ | ||
139 | + public function intersect($items, $indexKey = null) | ||
140 | + { | ||
141 | + if ($this->isEmpty() || is_scalar($this->items[0])) { | ||
142 | + return new static(array_diff($this->items, $this->convertToArray($items))); | ||
143 | + } | ||
144 | + | ||
145 | + $intersect = []; | ||
146 | + $dictionary = $this->dictionary($items, $indexKey); | ||
147 | + | ||
148 | + if (is_string($indexKey)) { | ||
149 | + foreach ($this->items as $item) { | ||
150 | + if (isset($dictionary[$item[$indexKey]])) { | ||
151 | + $intersect[] = $item; | ||
152 | + } | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + return new static($intersect); | ||
157 | + } | ||
158 | + | ||
159 | + /** | ||
160 | + * 返回数组中所有的键名 | ||
161 | + * | ||
162 | + * @access public | ||
163 | + * @return array | ||
164 | + */ | ||
165 | + public function keys() | ||
166 | + { | ||
167 | + $current = current($this->items); | ||
168 | + | ||
169 | + if (is_scalar($current)) { | ||
170 | + $array = $this->items; | ||
171 | + } elseif (is_array($current)) { | ||
172 | + $array = $current; | ||
173 | + } else { | ||
174 | + $array = $current->toArray(); | ||
175 | + } | ||
176 | + | ||
177 | + return array_keys($array); | ||
178 | + } | ||
179 | + | ||
180 | + /** | ||
181 | + * 删除数组的最后一个元素(出栈) | ||
182 | + * | ||
183 | + * @return mixed | ||
184 | + */ | ||
185 | + public function pop() | ||
186 | + { | ||
187 | + return array_pop($this->items); | ||
188 | + } | ||
189 | + | ||
190 | + /** | ||
191 | + * 通过使用用户自定义函数,以字符串返回数组 | ||
192 | + * | ||
193 | + * @param callable $callback | ||
194 | + * @param mixed $initial | ||
195 | + * @return mixed | ||
196 | + */ | ||
197 | + public function reduce(callable $callback, $initial = null) | ||
198 | + { | ||
199 | + return array_reduce($this->items, $callback, $initial); | ||
200 | + } | ||
201 | + | ||
202 | + /** | ||
203 | + * 以相反的顺序返回数组。 | ||
204 | + * | ||
205 | + * @return static | ||
206 | + */ | ||
207 | + public function reverse() | ||
208 | + { | ||
209 | + return new static(array_reverse($this->items)); | ||
210 | + } | ||
211 | + | ||
212 | + /** | ||
213 | + * 删除数组中首个元素,并返回被删除元素的值 | ||
214 | + * | ||
215 | + * @return mixed | ||
216 | + */ | ||
217 | + public function shift() | ||
218 | + { | ||
219 | + return array_shift($this->items); | ||
220 | + } | ||
221 | + | ||
222 | + /** | ||
223 | + * 在数组结尾插入一个元素 | ||
224 | + * @param mixed $value | ||
225 | + * @param mixed $key | ||
226 | + * @return void | ||
227 | + */ | ||
228 | + public function push($value, $key = null) | ||
229 | + { | ||
230 | + if (is_null($key)) { | ||
231 | + $this->items[] = $value; | ||
232 | + } else { | ||
233 | + $this->items[$key] = $value; | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
237 | + /** | ||
238 | + * 把一个数组分割为新的数组块. | ||
239 | + * | ||
240 | + * @param int $size | ||
241 | + * @param bool $preserveKeys | ||
242 | + * @return static | ||
243 | + */ | ||
244 | + public function chunk($size, $preserveKeys = false) | ||
245 | + { | ||
246 | + $chunks = []; | ||
247 | + | ||
248 | + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { | ||
249 | + $chunks[] = new static($chunk); | ||
250 | + } | ||
251 | + | ||
252 | + return new static($chunks); | ||
253 | + } | ||
254 | + | ||
255 | + /** | ||
256 | + * 在数组开头插入一个元素 | ||
257 | + * @param mixed $value | ||
258 | + * @param mixed $key | ||
259 | + * @return void | ||
260 | + */ | ||
261 | + public function unshift($value, $key = null) | ||
262 | + { | ||
263 | + if (is_null($key)) { | ||
264 | + array_unshift($this->items, $value); | ||
265 | + } else { | ||
266 | + $this->items = [$key => $value] + $this->items; | ||
267 | + } | ||
268 | + } | ||
269 | + | ||
270 | + /** | ||
271 | + * 给每个元素执行个回调 | ||
272 | + * | ||
273 | + * @param callable $callback | ||
274 | + * @return $this | ||
275 | + */ | ||
276 | + public function each(callable $callback) | ||
277 | + { | ||
278 | + foreach ($this->items as $key => $item) { | ||
279 | + $result = $callback($item, $key); | ||
280 | + | ||
281 | + if (false === $result) { | ||
282 | + break; | ||
283 | + } elseif (!is_object($item)) { | ||
284 | + $this->items[$key] = $result; | ||
285 | + } | ||
286 | + } | ||
287 | + | ||
288 | + return $this; | ||
289 | + } | ||
290 | + | ||
291 | + /** | ||
292 | + * 用回调函数过滤数组中的元素 | ||
293 | + * @param callable|null $callback | ||
294 | + * @return static | ||
295 | + */ | ||
296 | + public function filter(callable $callback = null) | ||
297 | + { | ||
298 | + if ($callback) { | ||
299 | + return new static(array_filter($this->items, $callback)); | ||
300 | + } | ||
301 | + | ||
302 | + return new static(array_filter($this->items)); | ||
303 | + } | ||
304 | + | ||
305 | + /** | ||
306 | + * 根据字段条件过滤数组中的元素 | ||
307 | + * @access public | ||
308 | + * @param string $field 字段名 | ||
309 | + * @param mixed $operator 操作符 | ||
310 | + * @param mixed $value 数据 | ||
311 | + * @return static | ||
312 | + */ | ||
313 | + public function where($field, $operator, $value = null) | ||
314 | + { | ||
315 | + if (is_null($value)) { | ||
316 | + $value = $operator; | ||
317 | + $operator = '='; | ||
318 | + } | ||
319 | + | ||
320 | + return $this->filter(function ($data) use ($field, $operator, $value) { | ||
321 | + if (strpos($field, '.')) { | ||
322 | + list($field, $relation) = explode('.', $field); | ||
323 | + | ||
324 | + $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null; | ||
325 | + } else { | ||
326 | + $result = isset($data[$field]) ? $data[$field] : null; | ||
327 | + } | ||
328 | + | ||
329 | + switch ($operator) { | ||
330 | + case '===': | ||
331 | + return $result === $value; | ||
332 | + case '!==': | ||
333 | + return $result !== $value; | ||
334 | + case '!=': | ||
335 | + case '<>': | ||
336 | + return $result != $value; | ||
337 | + case '>': | ||
338 | + return $result > $value; | ||
339 | + case '>=': | ||
340 | + return $result >= $value; | ||
341 | + case '<': | ||
342 | + return $result < $value; | ||
343 | + case '<=': | ||
344 | + return $result <= $value; | ||
345 | + case 'like': | ||
346 | + return is_string($result) && false !== strpos($result, $value); | ||
347 | + case 'not like': | ||
348 | + return is_string($result) && false === strpos($result, $value); | ||
349 | + case 'in': | ||
350 | + return is_scalar($result) && in_array($result, $value, true); | ||
351 | + case 'not in': | ||
352 | + return is_scalar($result) && !in_array($result, $value, true); | ||
353 | + case 'between': | ||
354 | + list($min, $max) = is_string($value) ? explode(',', $value) : $value; | ||
355 | + return is_scalar($result) && $result >= $min && $result <= $max; | ||
356 | + case 'not between': | ||
357 | + list($min, $max) = is_string($value) ? explode(',', $value) : $value; | ||
358 | + return is_scalar($result) && $result > $max || $result < $min; | ||
359 | + case '==': | ||
360 | + case '=': | ||
361 | + default: | ||
362 | + return $result == $value; | ||
363 | + } | ||
364 | + }); | ||
365 | + } | ||
366 | + | ||
367 | + /** | ||
368 | + * 返回数组中指定的一列 | ||
369 | + * @param mixed $column_key | ||
370 | + * @param mixed $index_key | ||
371 | + * @return array | ||
372 | + */ | ||
373 | + public function column($column_key, $index_key = null) | ||
374 | + { | ||
375 | + return array_column($this->items, $column_key, $index_key); | ||
376 | + } | ||
377 | + | ||
378 | + /** | ||
379 | + * 对数组排序 | ||
380 | + * | ||
381 | + * @access public | ||
382 | + * @param callable|null $callback | ||
383 | + * @return static | ||
384 | + */ | ||
385 | + public function sort(callable $callback = null) | ||
386 | + { | ||
387 | + $items = $this->items; | ||
388 | + | ||
389 | + $callback = $callback ?: function ($a, $b) { | ||
390 | + return $a == $b ? 0 : (($a < $b) ? -1 : 1); | ||
391 | + | ||
392 | + }; | ||
393 | + | ||
394 | + uasort($items, $callback); | ||
395 | + | ||
396 | + return new static($items); | ||
397 | + } | ||
398 | + | ||
399 | + /** | ||
400 | + * 指定字段排序 | ||
401 | + * @access public | ||
402 | + * @param string $field 排序字段 | ||
403 | + * @param string $order 排序 | ||
404 | + * @param bool $intSort 是否为数字排序 | ||
405 | + * @return $this | ||
406 | + */ | ||
407 | + public function order($field, $order = null, $intSort = true) | ||
408 | + { | ||
409 | + return $this->sort(function ($a, $b) use ($field, $order, $intSort) { | ||
410 | + $fieldA = isset($a[$field]) ? $a[$field] : null; | ||
411 | + $fieldB = isset($b[$field]) ? $b[$field] : null; | ||
412 | + | ||
413 | + if ($intSort) { | ||
414 | + return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB; | ||
415 | + } else { | ||
416 | + return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); | ||
417 | + } | ||
418 | + }); | ||
419 | + } | ||
420 | + | ||
421 | + /** | ||
422 | + * 将数组打乱 | ||
423 | + * | ||
424 | + * @return static | ||
425 | + */ | ||
426 | + public function shuffle() | ||
427 | + { | ||
428 | + $items = $this->items; | ||
429 | + | ||
430 | + shuffle($items); | ||
431 | + | ||
432 | + return new static($items); | ||
433 | + } | ||
434 | + | ||
435 | + /** | ||
436 | + * 截取数组 | ||
437 | + * | ||
438 | + * @param int $offset | ||
439 | + * @param int $length | ||
440 | + * @param bool $preserveKeys | ||
441 | + * @return static | ||
442 | + */ | ||
443 | + public function slice($offset, $length = null, $preserveKeys = false) | ||
444 | + { | ||
445 | + return new static(array_slice($this->items, $offset, $length, $preserveKeys)); | ||
446 | + } | ||
447 | + | ||
448 | + // ArrayAccess | ||
449 | + public function offsetExists($offset) | ||
450 | + { | ||
451 | + return array_key_exists($offset, $this->items); | ||
452 | + } | ||
453 | + | ||
454 | + public function offsetGet($offset) | ||
455 | + { | ||
456 | + return $this->items[$offset]; | ||
457 | + } | ||
458 | + | ||
459 | + public function offsetSet($offset, $value) | ||
460 | + { | ||
461 | + if (is_null($offset)) { | ||
462 | + $this->items[] = $value; | ||
463 | + } else { | ||
464 | + $this->items[$offset] = $value; | ||
465 | + } | ||
466 | + } | ||
467 | + | ||
468 | + public function offsetUnset($offset) | ||
469 | + { | ||
470 | + unset($this->items[$offset]); | ||
471 | + } | ||
472 | + | ||
473 | + //Countable | ||
474 | + public function count() | ||
475 | + { | ||
476 | + return count($this->items); | ||
477 | + } | ||
478 | + | ||
479 | + //IteratorAggregate | ||
480 | + public function getIterator() | ||
481 | + { | ||
482 | + return new ArrayIterator($this->items); | ||
483 | + } | ||
484 | + | ||
485 | + //JsonSerializable | ||
486 | + public function jsonSerialize() | ||
487 | + { | ||
488 | + return $this->toArray(); | ||
489 | + } | ||
490 | + | ||
491 | + /** | ||
492 | + * 转换当前数据集为JSON字符串 | ||
493 | + * @access public | ||
494 | + * @param integer $options json参数 | ||
495 | + * @return string | ||
496 | + */ | ||
497 | + public function toJson($options = JSON_UNESCAPED_UNICODE) | ||
498 | + { | ||
499 | + return json_encode($this->toArray(), $options); | ||
500 | + } | ||
501 | + | ||
502 | + public function __toString() | ||
503 | + { | ||
504 | + return $this->toJson(); | ||
505 | + } | ||
506 | + | ||
507 | + /** | ||
508 | + * 转换成数组 | ||
509 | + * | ||
510 | + * @param mixed $items | ||
511 | + * @return array | ||
512 | + */ | ||
513 | + protected function convertToArray($items) | ||
514 | + { | ||
515 | + if ($items instanceof self) { | ||
516 | + return $items->all(); | ||
517 | + } | ||
518 | + return (array) $items; | ||
519 | + } | ||
520 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: liu21st <liu21st@gmail.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think; | ||
13 | + | ||
14 | +use think\db\Query; | ||
15 | + | ||
16 | +/** | ||
17 | + * Class Db | ||
18 | + * @package think | ||
19 | + * @method Query table(string $table) static 指定数据表(含前缀) | ||
20 | + * @method Query name(string $name) static 指定数据表(不含前缀) | ||
21 | + * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 | ||
22 | + * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 | ||
23 | + * @method Query union(mixed $union, boolean $all = false) static UNION查询 | ||
24 | + * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT | ||
25 | + * @method Query order(mixed $field, string $order = null) static 查询ORDER | ||
26 | + * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 | ||
27 | + * @method mixed value(string $field) static 获取某个字段的值 | ||
28 | + * @method array column(string $field, string $key = '') static 获取某个列的值 | ||
29 | + * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 | ||
30 | + * @method mixed find(mixed $data = null) static 查询单个记录 | ||
31 | + * @method mixed select(mixed $data = null) static 查询多个记录 | ||
32 | + * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 | ||
33 | + * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID | ||
34 | + * @method integer insertAll(array $dataSet) static 插入多条记录 | ||
35 | + * @method integer update(array $data) static 更新记录 | ||
36 | + * @method integer delete(mixed $data = null) static 删除记录 | ||
37 | + * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 | ||
38 | + * @method \Generator cursor(mixed $data = null) static 使用游标查找记录 | ||
39 | + * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 | ||
40 | + * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 | ||
41 | + * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 | ||
42 | + * @method mixed transaction(callable $callback) static 执行数据库事务 | ||
43 | + * @method void startTrans() static 启动事务 | ||
44 | + * @method void commit() static 用于非自动提交状态下面的查询提交 | ||
45 | + * @method void rollback() static 事务回滚 | ||
46 | + * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 | ||
47 | + * @method string getLastInsID($sequence = null) static 获取最近插入的ID | ||
48 | + */ | ||
49 | +class Db | ||
50 | +{ | ||
51 | + /** | ||
52 | + * 数据库配置 | ||
53 | + * @var array | ||
54 | + */ | ||
55 | + protected static $config = []; | ||
56 | + | ||
57 | + /** | ||
58 | + * 查询类名 | ||
59 | + * @var string | ||
60 | + */ | ||
61 | + protected static $query; | ||
62 | + | ||
63 | + /** | ||
64 | + * 查询类自动映射 | ||
65 | + * @var array | ||
66 | + */ | ||
67 | + protected static $queryMap = [ | ||
68 | + 'mongo' => '\\think\\db\Mongo', | ||
69 | + ]; | ||
70 | + | ||
71 | + /** | ||
72 | + * 查询次数 | ||
73 | + * @var integer | ||
74 | + */ | ||
75 | + public static $queryTimes = 0; | ||
76 | + | ||
77 | + /** | ||
78 | + * 执行次数 | ||
79 | + * @var integer | ||
80 | + */ | ||
81 | + public static $executeTimes = 0; | ||
82 | + | ||
83 | + /** | ||
84 | + * 缓存对象 | ||
85 | + * @var object | ||
86 | + */ | ||
87 | + protected static $cacheHandler; | ||
88 | + | ||
89 | + public static function setConfig($config = []) | ||
90 | + { | ||
91 | + self::$config = array_merge(self::$config, $config); | ||
92 | + } | ||
93 | + | ||
94 | + public static function getConfig($name = null) | ||
95 | + { | ||
96 | + if ($name) { | ||
97 | + return isset(self::$config[$name]) ? self::$config[$name] : null; | ||
98 | + } else { | ||
99 | + return self::$config; | ||
100 | + } | ||
101 | + } | ||
102 | + | ||
103 | + public static function setQuery($query) | ||
104 | + { | ||
105 | + self::$query = $query; | ||
106 | + } | ||
107 | + | ||
108 | + /** | ||
109 | + * 字符串命名风格转换 | ||
110 | + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 | ||
111 | + * @param string $name 字符串 | ||
112 | + * @param integer $type 转换类型 | ||
113 | + * @param bool $ucfirst 首字母是否大写(驼峰规则) | ||
114 | + * @return string | ||
115 | + */ | ||
116 | + public static function parseName($name, $type = 0, $ucfirst = true) | ||
117 | + { | ||
118 | + if ($type) { | ||
119 | + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { | ||
120 | + return strtoupper($match[1]); | ||
121 | + }, $name); | ||
122 | + return $ucfirst ? ucfirst($name) : lcfirst($name); | ||
123 | + } else { | ||
124 | + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + public static function setCacheHandler($cacheHandler) | ||
129 | + { | ||
130 | + self::$cacheHandler = $cacheHandler; | ||
131 | + } | ||
132 | + | ||
133 | + public static function getCacheHandler() | ||
134 | + { | ||
135 | + return self::$cacheHandler; | ||
136 | + } | ||
137 | + | ||
138 | + public static function __callStatic($method, $args) | ||
139 | + { | ||
140 | + if (!self::$query) { | ||
141 | + $type = strtolower(self::getConfig('type')); | ||
142 | + | ||
143 | + $class = isset(self::$queryMap[$type]) ? self::$queryMap[$type] : '\\think\\db\\Query'; | ||
144 | + | ||
145 | + self::$query = $class; | ||
146 | + } | ||
147 | + | ||
148 | + $class = self::$query; | ||
149 | + | ||
150 | + return call_user_func_array([new $class, $method], $args); | ||
151 | + } | ||
152 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think; | ||
13 | + | ||
14 | +class Exception extends \Exception | ||
15 | +{ | ||
16 | + | ||
17 | + /** | ||
18 | + * 保存异常页面显示的额外Debug数据 | ||
19 | + * @var array | ||
20 | + */ | ||
21 | + protected $data = []; | ||
22 | + | ||
23 | + /** | ||
24 | + * 设置异常额外的Debug数据 | ||
25 | + * 数据将会显示为下面的格式 | ||
26 | + * | ||
27 | + * Exception Data | ||
28 | + * -------------------------------------------------- | ||
29 | + * Label 1 | ||
30 | + * key1 value1 | ||
31 | + * key2 value2 | ||
32 | + * Label 2 | ||
33 | + * key1 value1 | ||
34 | + * key2 value2 | ||
35 | + * | ||
36 | + * @param string $label 数据分类,用于异常页面显示 | ||
37 | + * @param array $data 需要显示的数据,必须为关联数组 | ||
38 | + */ | ||
39 | + final protected function setData($label, array $data) | ||
40 | + { | ||
41 | + $this->data[$label] = $data; | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * 获取异常额外Debug数据 | ||
46 | + * 主要用于输出到异常页面便于调试 | ||
47 | + * @return array 由setData设置的Debug数据 | ||
48 | + */ | ||
49 | + final public function getData() | ||
50 | + { | ||
51 | + return $this->data; | ||
52 | + } | ||
53 | + | ||
54 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: liu21st <liu21st@gmail.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think; | ||
13 | + | ||
14 | +use think\db\Query; | ||
15 | + | ||
16 | +/** | ||
17 | + * Class Model | ||
18 | + * @package think | ||
19 | + * @mixin Query | ||
20 | + * @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器 | ||
21 | + */ | ||
22 | +abstract class Model implements \JsonSerializable, \ArrayAccess | ||
23 | +{ | ||
24 | + use model\concern\Attribute; | ||
25 | + use model\concern\RelationShip; | ||
26 | + use model\concern\ModelEvent; | ||
27 | + use model\concern\TimeStamp; | ||
28 | + use model\concern\Conversion; | ||
29 | + | ||
30 | + /** | ||
31 | + * 数据是否存在 | ||
32 | + * @var bool | ||
33 | + */ | ||
34 | + private $exists = false; | ||
35 | + | ||
36 | + /** | ||
37 | + * 是否强制更新所有数据 | ||
38 | + * @var bool | ||
39 | + */ | ||
40 | + private $force = false; | ||
41 | + | ||
42 | + /** | ||
43 | + * 更新条件 | ||
44 | + * @var array | ||
45 | + */ | ||
46 | + private $updateWhere; | ||
47 | + | ||
48 | + /** | ||
49 | + * 数据库配置信息 | ||
50 | + * @var array|string | ||
51 | + */ | ||
52 | + protected $connection = []; | ||
53 | + | ||
54 | + /** | ||
55 | + * 数据库查询对象类名 | ||
56 | + * @var string | ||
57 | + */ | ||
58 | + protected $query; | ||
59 | + | ||
60 | + /** | ||
61 | + * 模型名称 | ||
62 | + * @var string | ||
63 | + */ | ||
64 | + protected $name; | ||
65 | + | ||
66 | + /** | ||
67 | + * 数据表名称 | ||
68 | + * @var string | ||
69 | + */ | ||
70 | + protected $table; | ||
71 | + | ||
72 | + /** | ||
73 | + * 写入自动完成定义 | ||
74 | + * @var array | ||
75 | + */ | ||
76 | + protected $auto = []; | ||
77 | + | ||
78 | + /** | ||
79 | + * 新增自动完成定义 | ||
80 | + * @var array | ||
81 | + */ | ||
82 | + protected $insert = []; | ||
83 | + | ||
84 | + /** | ||
85 | + * 更新自动完成定义 | ||
86 | + * @var array | ||
87 | + */ | ||
88 | + protected $update = []; | ||
89 | + | ||
90 | + /** | ||
91 | + * 初始化过的模型. | ||
92 | + * @var array | ||
93 | + */ | ||
94 | + protected static $initialized = []; | ||
95 | + | ||
96 | + /** | ||
97 | + * 是否从主库读取(主从分布式有效) | ||
98 | + * @var array | ||
99 | + */ | ||
100 | + protected static $readMaster; | ||
101 | + | ||
102 | + /** | ||
103 | + * 查询对象实例 | ||
104 | + * @var Query | ||
105 | + */ | ||
106 | + protected $queryInstance; | ||
107 | + | ||
108 | + /** | ||
109 | + * 错误信息 | ||
110 | + * @var mixed | ||
111 | + */ | ||
112 | + protected $error; | ||
113 | + | ||
114 | + /** | ||
115 | + * 软删除字段默认值 | ||
116 | + * @var mixed | ||
117 | + */ | ||
118 | + protected $defaultSoftDelete; | ||
119 | + | ||
120 | + /** | ||
121 | + * 全局查询范围 | ||
122 | + * @var array | ||
123 | + */ | ||
124 | + protected $globalScope = []; | ||
125 | + | ||
126 | + /** | ||
127 | + * 架构函数 | ||
128 | + * @access public | ||
129 | + * @param array|object $data 数据 | ||
130 | + */ | ||
131 | + public function __construct($data = []) | ||
132 | + { | ||
133 | + if (is_object($data)) { | ||
134 | + $this->data = get_object_vars($data); | ||
135 | + } else { | ||
136 | + $this->data = $data; | ||
137 | + } | ||
138 | + | ||
139 | + if ($this->disuse) { | ||
140 | + // 废弃字段 | ||
141 | + foreach ((array) $this->disuse as $key) { | ||
142 | + if (array_key_exists($key, $this->data)) { | ||
143 | + unset($this->data[$key]); | ||
144 | + } | ||
145 | + } | ||
146 | + } | ||
147 | + | ||
148 | + // 记录原始数据 | ||
149 | + $this->origin = $this->data; | ||
150 | + | ||
151 | + $config = Db::getConfig(); | ||
152 | + | ||
153 | + if (empty($this->name)) { | ||
154 | + // 当前模型名 | ||
155 | + $name = str_replace('\\', '/', static::class); | ||
156 | + $this->name = basename($name); | ||
157 | + if (!empty($config['class_suffix'])) { | ||
158 | + $suffix = basename(dirname($name)); | ||
159 | + $this->name = substr($this->name, 0, -strlen($suffix)); | ||
160 | + } | ||
161 | + } | ||
162 | + | ||
163 | + if (is_null($this->autoWriteTimestamp)) { | ||
164 | + // 自动写入时间戳 | ||
165 | + $this->autoWriteTimestamp = $config['auto_timestamp']; | ||
166 | + } | ||
167 | + | ||
168 | + if (is_null($this->dateFormat)) { | ||
169 | + // 设置时间戳格式 | ||
170 | + $this->dateFormat = $config['datetime_format']; | ||
171 | + } | ||
172 | + | ||
173 | + if (is_null($this->resultSetType)) { | ||
174 | + $this->resultSetType = $config['resultset_type']; | ||
175 | + } | ||
176 | + | ||
177 | + if (is_null($this->query)) { | ||
178 | + // 设置查询对象 | ||
179 | + $this->query = $config['query']; | ||
180 | + } | ||
181 | + | ||
182 | + if (!empty($this->connection) && is_array($this->connection)) { | ||
183 | + // 设置模型的数据库连接 | ||
184 | + $this->connection = array_merge($config, $this->connection); | ||
185 | + } | ||
186 | + | ||
187 | + if ($this->observerClass) { | ||
188 | + // 注册模型观察者 | ||
189 | + static::observe($this->observerClass); | ||
190 | + } | ||
191 | + | ||
192 | + // 执行初始化操作 | ||
193 | + $this->initialize(); | ||
194 | + } | ||
195 | + | ||
196 | + /** | ||
197 | + * 是否从主库读取数据(主从分布有效) | ||
198 | + * @access public | ||
199 | + * @param bool $all 是否所有模型有效 | ||
200 | + * @return $this | ||
201 | + */ | ||
202 | + public function readMaster($all = false) | ||
203 | + { | ||
204 | + $model = $all ? '*' : static::class; | ||
205 | + | ||
206 | + static::$readMaster[$model] = true; | ||
207 | + | ||
208 | + return $this; | ||
209 | + } | ||
210 | + | ||
211 | + /** | ||
212 | + * 创建新的模型实例 | ||
213 | + * @access public | ||
214 | + * @param array|object $data 数据 | ||
215 | + * @param bool $isUpdate 是否为更新 | ||
216 | + * @param mixed $where 更新条件 | ||
217 | + * @return Model | ||
218 | + */ | ||
219 | + public function newInstance($data = [], $isUpdate = false, $where = null) | ||
220 | + { | ||
221 | + return (new static($data))->isUpdate($isUpdate, $where); | ||
222 | + } | ||
223 | + | ||
224 | + /** | ||
225 | + * 创建模型的查询对象 | ||
226 | + * @access protected | ||
227 | + * @return Query | ||
228 | + */ | ||
229 | + protected function buildQuery() | ||
230 | + { | ||
231 | + // 设置当前模型 确保查询返回模型对象 | ||
232 | + $class = $this->query; | ||
233 | + $query = (new $class())->connect($this->connection) | ||
234 | + ->model($this) | ||
235 | + ->json($this->json, $this->jsonAssoc) | ||
236 | + ->setJsonFieldType($this->jsonType); | ||
237 | + | ||
238 | + if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) { | ||
239 | + $query->master(true); | ||
240 | + } | ||
241 | + | ||
242 | + // 设置当前数据表和模型名 | ||
243 | + if (!empty($this->table)) { | ||
244 | + $query->table($this->table); | ||
245 | + } else { | ||
246 | + $query->name($this->name); | ||
247 | + } | ||
248 | + | ||
249 | + if (!empty($this->pk)) { | ||
250 | + $query->pk($this->pk); | ||
251 | + } | ||
252 | + | ||
253 | + return $query; | ||
254 | + } | ||
255 | + | ||
256 | + /** | ||
257 | + * 获取当前模型的数据库查询对象 | ||
258 | + * @access public | ||
259 | + * @param Query $query 查询对象实例 | ||
260 | + * @return $this | ||
261 | + */ | ||
262 | + public function setQuery($query) | ||
263 | + { | ||
264 | + $this->queryInstance = $query; | ||
265 | + return $this; | ||
266 | + } | ||
267 | + | ||
268 | + /** | ||
269 | + * 获取当前模型的数据库查询对象 | ||
270 | + * @access public | ||
271 | + * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称) | ||
272 | + * @return Query | ||
273 | + */ | ||
274 | + public function db($useBaseQuery = true) | ||
275 | + { | ||
276 | + if ($this->queryInstance) { | ||
277 | + return $this->queryInstance; | ||
278 | + } | ||
279 | + | ||
280 | + $query = $this->buildQuery(); | ||
281 | + | ||
282 | + // 软删除 | ||
283 | + if (property_exists($this, 'withTrashed') && !$this->withTrashed) { | ||
284 | + $this->withNoTrashed($query); | ||
285 | + } | ||
286 | + | ||
287 | + // 全局作用域 | ||
288 | + if (true === $useBaseQuery && method_exists($this, 'base')) { | ||
289 | + call_user_func_array([$this, 'base'], [ & $query]); | ||
290 | + } | ||
291 | + | ||
292 | + $globalScope = is_array($useBaseQuery) && $useBaseQuery ?: $this->globalScope; | ||
293 | + | ||
294 | + if ($globalScope) { | ||
295 | + $query->scope($globalScope); | ||
296 | + } | ||
297 | + | ||
298 | + // 返回当前模型的数据库查询对象 | ||
299 | + return $query; | ||
300 | + } | ||
301 | + | ||
302 | + /** | ||
303 | + * 初始化模型 | ||
304 | + * @access protected | ||
305 | + * @return void | ||
306 | + */ | ||
307 | + protected function initialize() | ||
308 | + { | ||
309 | + if (!isset(static::$initialized[static::class])) { | ||
310 | + static::$initialized[static::class] = true; | ||
311 | + static::init(); | ||
312 | + } | ||
313 | + } | ||
314 | + | ||
315 | + /** | ||
316 | + * 初始化处理 | ||
317 | + * @access protected | ||
318 | + * @return void | ||
319 | + */ | ||
320 | + protected static function init() | ||
321 | + {} | ||
322 | + | ||
323 | + /** | ||
324 | + * 更新是否强制写入数据 而不做比较 | ||
325 | + * @access public | ||
326 | + * @param bool $force | ||
327 | + * @return $this | ||
328 | + */ | ||
329 | + public function force($force = true) | ||
330 | + { | ||
331 | + $this->force = $force; | ||
332 | + return $this; | ||
333 | + } | ||
334 | + | ||
335 | + /** | ||
336 | + * 判断force | ||
337 | + * @access public | ||
338 | + * @return bool | ||
339 | + */ | ||
340 | + public function isForce() | ||
341 | + { | ||
342 | + return $this->force; | ||
343 | + } | ||
344 | + | ||
345 | + /** | ||
346 | + * 设置数据是否存在 | ||
347 | + * @access public | ||
348 | + * @param bool $exists | ||
349 | + * @return $this | ||
350 | + */ | ||
351 | + public function exists($exists) | ||
352 | + { | ||
353 | + $this->exists = $exists; | ||
354 | + return $this; | ||
355 | + } | ||
356 | + | ||
357 | + /** | ||
358 | + * 判断数据是否存在数据库 | ||
359 | + * @access public | ||
360 | + * @return bool | ||
361 | + */ | ||
362 | + public function isExists() | ||
363 | + { | ||
364 | + return $this->exists; | ||
365 | + } | ||
366 | + | ||
367 | + /** | ||
368 | + * 数据自动完成 | ||
369 | + * @access protected | ||
370 | + * @param array $auto 要自动更新的字段列表 | ||
371 | + * @return void | ||
372 | + */ | ||
373 | + protected function autoCompleteData($auto = []) | ||
374 | + { | ||
375 | + foreach ($auto as $field => $value) { | ||
376 | + if (is_integer($field)) { | ||
377 | + $field = $value; | ||
378 | + $value = null; | ||
379 | + } | ||
380 | + | ||
381 | + if (!isset($this->data[$field])) { | ||
382 | + $default = null; | ||
383 | + } else { | ||
384 | + $default = $this->data[$field]; | ||
385 | + } | ||
386 | + | ||
387 | + $this->setAttr($field, !is_null($value) ? $value : $default); | ||
388 | + } | ||
389 | + } | ||
390 | + | ||
391 | + /** | ||
392 | + * 保存当前数据对象 | ||
393 | + * @access public | ||
394 | + * @param array $data 数据 | ||
395 | + * @param array $where 更新条件 | ||
396 | + * @param string $sequence 自增序列名 | ||
397 | + * @return false | ||
398 | + */ | ||
399 | + public function save($data = [], $where = [], $sequence = null) | ||
400 | + { | ||
401 | + if (is_string($data)) { | ||
402 | + $sequence = $data; | ||
403 | + $data = []; | ||
404 | + } | ||
405 | + | ||
406 | + if (!$this->checkBeforeSave($data, $where)) { | ||
407 | + return false; | ||
408 | + } | ||
409 | + | ||
410 | + $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence); | ||
411 | + | ||
412 | + if (false === $result) { | ||
413 | + return false; | ||
414 | + } | ||
415 | + | ||
416 | + // 写入回调 | ||
417 | + $this->trigger('after_write'); | ||
418 | + | ||
419 | + // 重新记录原始数据 | ||
420 | + $this->origin = $this->data; | ||
421 | + $this->set = []; | ||
422 | + | ||
423 | + return true; | ||
424 | + } | ||
425 | + | ||
426 | + /** | ||
427 | + * 解析查询条件 | ||
428 | + * @access protected | ||
429 | + * @param array|null $where 保存条件 | ||
430 | + * @return array|null | ||
431 | + */ | ||
432 | + protected static function parseWhere($where) | ||
433 | + { | ||
434 | + if (is_array($where) && key($where) !== 0) { | ||
435 | + $item = []; | ||
436 | + foreach ($where as $key => $val) { | ||
437 | + $item[] = [$key, '=', $val]; | ||
438 | + } | ||
439 | + return $item; | ||
440 | + } | ||
441 | + return $where; | ||
442 | + } | ||
443 | + | ||
444 | + /** | ||
445 | + * 写入之前检查数据 | ||
446 | + * @access protected | ||
447 | + * @param array $data 数据 | ||
448 | + * @param array $where 保存条件 | ||
449 | + * @return bool | ||
450 | + */ | ||
451 | + protected function checkBeforeSave($data, $where) | ||
452 | + { | ||
453 | + if (!empty($data)) { | ||
454 | + | ||
455 | + // 数据对象赋值 | ||
456 | + foreach ($data as $key => $value) { | ||
457 | + $this->setAttr($key, $value, $data); | ||
458 | + } | ||
459 | + | ||
460 | + if (!empty($where)) { | ||
461 | + $this->exists = true; | ||
462 | + $this->updateWhere = self::parseWhere($where); | ||
463 | + } | ||
464 | + } | ||
465 | + | ||
466 | + // 数据自动完成 | ||
467 | + $this->autoCompleteData($this->auto); | ||
468 | + | ||
469 | + // 事件回调 | ||
470 | + if (false === $this->trigger('before_write')) { | ||
471 | + return false; | ||
472 | + } | ||
473 | + | ||
474 | + return true; | ||
475 | + } | ||
476 | + | ||
477 | + /** | ||
478 | + * 检查数据是否允许写入 | ||
479 | + * @access protected | ||
480 | + * @param array $autoFields 自动完成的字段列表 | ||
481 | + * @return array | ||
482 | + */ | ||
483 | + protected function checkAllowFields($append = []) | ||
484 | + { | ||
485 | + // 检测字段 | ||
486 | + if (empty($this->field) || true === $this->field) { | ||
487 | + $query = $this->db(false); | ||
488 | + $table = $this->table ?: $query->getTable(); | ||
489 | + | ||
490 | + $this->field = $query->getConnection()->getTableFields($table); | ||
491 | + | ||
492 | + $field = $this->field; | ||
493 | + } else { | ||
494 | + $field = array_merge($this->field, $append); | ||
495 | + | ||
496 | + if ($this->autoWriteTimestamp) { | ||
497 | + array_push($field, $this->createTime, $this->updateTime); | ||
498 | + } | ||
499 | + } | ||
500 | + | ||
501 | + if ($this->disuse) { | ||
502 | + // 废弃字段 | ||
503 | + $field = array_diff($field, (array) $this->disuse); | ||
504 | + } | ||
505 | + return $field; | ||
506 | + } | ||
507 | + | ||
508 | + /** | ||
509 | + * 保存写入数据 | ||
510 | + * @access protected | ||
511 | + * @param array $where 保存条件 | ||
512 | + * @return int|false | ||
513 | + */ | ||
514 | + protected function updateData($where) | ||
515 | + { | ||
516 | + // 自动更新 | ||
517 | + $this->autoCompleteData($this->update); | ||
518 | + | ||
519 | + // 事件回调 | ||
520 | + if (false === $this->trigger('before_update')) { | ||
521 | + return false; | ||
522 | + } | ||
523 | + | ||
524 | + // 获取有更新的数据 | ||
525 | + $data = $this->getChangedData(); | ||
526 | + | ||
527 | + if (empty($data)) { | ||
528 | + // 关联更新 | ||
529 | + if (isset($this->relationWrite)) { | ||
530 | + $this->autoRelationUpdate(); | ||
531 | + } | ||
532 | + | ||
533 | + return 0; | ||
534 | + } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { | ||
535 | + // 自动写入更新时间 | ||
536 | + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); | ||
537 | + | ||
538 | + $this->data[$this->updateTime] = $data[$this->updateTime]; | ||
539 | + } | ||
540 | + | ||
541 | + if (empty($where) && !empty($this->updateWhere)) { | ||
542 | + $where = $this->updateWhere; | ||
543 | + } | ||
544 | + | ||
545 | + // 检查允许字段 | ||
546 | + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update)); | ||
547 | + | ||
548 | + // 保留主键数据 | ||
549 | + foreach ($this->data as $key => $val) { | ||
550 | + if ($this->isPk($key)) { | ||
551 | + $data[$key] = $val; | ||
552 | + } | ||
553 | + } | ||
554 | + | ||
555 | + $pk = $this->getPk(); | ||
556 | + | ||
557 | + foreach ((array) $pk as $key) { | ||
558 | + if (isset($data[$key])) { | ||
559 | + $array[] = [$key, '=', $data[$key]]; | ||
560 | + unset($data[$key]); | ||
561 | + } | ||
562 | + } | ||
563 | + | ||
564 | + if (!empty($array)) { | ||
565 | + $where = $array; | ||
566 | + } | ||
567 | + | ||
568 | + if ($this->relationWrite) { | ||
569 | + foreach ($this->relationWrite as $name => $val) { | ||
570 | + if (is_array($val)) { | ||
571 | + foreach ($val as $key) { | ||
572 | + if (isset($data[$key])) { | ||
573 | + unset($data[$key]); | ||
574 | + } | ||
575 | + } | ||
576 | + } | ||
577 | + } | ||
578 | + } | ||
579 | + | ||
580 | + $db = $this->db(false); | ||
581 | + $db->startTrans(); | ||
582 | + | ||
583 | + try { | ||
584 | + // 模型更新 | ||
585 | + $result = $db->where($where) | ||
586 | + ->strict(false) | ||
587 | + ->field($allowFields) | ||
588 | + ->update($data); | ||
589 | + | ||
590 | + // 关联更新 | ||
591 | + if (isset($this->relationWrite)) { | ||
592 | + $this->autoRelationUpdate(); | ||
593 | + } | ||
594 | + | ||
595 | + $db->commit(); | ||
596 | + | ||
597 | + // 更新回调 | ||
598 | + $this->trigger('after_update'); | ||
599 | + | ||
600 | + return $result; | ||
601 | + } catch (\Exception $e) { | ||
602 | + $db->rollback(); | ||
603 | + throw $e; | ||
604 | + } | ||
605 | + } | ||
606 | + | ||
607 | + /** | ||
608 | + * 新增写入数据 | ||
609 | + * @access protected | ||
610 | + * @param string $sequence 自增名 | ||
611 | + * @return int|false | ||
612 | + */ | ||
613 | + protected function insertData($sequence) | ||
614 | + { | ||
615 | + // 自动写入 | ||
616 | + $this->autoCompleteData($this->insert); | ||
617 | + | ||
618 | + // 时间戳自动写入 | ||
619 | + $this->checkTimeStampWrite(); | ||
620 | + | ||
621 | + if (false === $this->trigger('before_insert')) { | ||
622 | + return false; | ||
623 | + } | ||
624 | + | ||
625 | + // 检查允许字段 | ||
626 | + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert)); | ||
627 | + | ||
628 | + $db = $this->db(false); | ||
629 | + $db->startTrans(); | ||
630 | + | ||
631 | + try { | ||
632 | + $result = $db->strict(false) | ||
633 | + ->field($allowFields) | ||
634 | + ->insert($this->data); | ||
635 | + | ||
636 | + // 获取自动增长主键 | ||
637 | + if ($result && $insertId = $db->getLastInsID($sequence)) { | ||
638 | + $pk = $this->getPk(); | ||
639 | + | ||
640 | + foreach ((array) $pk as $key) { | ||
641 | + if (!isset($this->data[$key]) || '' == $this->data[$key]) { | ||
642 | + $this->data[$key] = $insertId; | ||
643 | + } | ||
644 | + } | ||
645 | + } | ||
646 | + | ||
647 | + // 关联写入 | ||
648 | + if (isset($this->relationWrite)) { | ||
649 | + $this->autoRelationInsert(); | ||
650 | + } | ||
651 | + | ||
652 | + $db->commit(); | ||
653 | + | ||
654 | + // 标记为更新 | ||
655 | + $this->exists = true; | ||
656 | + | ||
657 | + // 新增回调 | ||
658 | + $this->trigger('after_insert'); | ||
659 | + | ||
660 | + return $result; | ||
661 | + } catch (\Exception $e) { | ||
662 | + $db->rollback(); | ||
663 | + throw $e; | ||
664 | + } | ||
665 | + } | ||
666 | + | ||
667 | + /** | ||
668 | + * 字段值(延迟)增长 | ||
669 | + * @access public | ||
670 | + * @param string $field 字段名 | ||
671 | + * @param integer $step 增长值 | ||
672 | + * @param integer $lazyTime 延时时间(s) | ||
673 | + * @return integer|true | ||
674 | + * @throws Exception | ||
675 | + */ | ||
676 | + public function setInc($field, $step = 1, $lazyTime = 0) | ||
677 | + { | ||
678 | + // 读取更新条件 | ||
679 | + $where = $this->getWhere(); | ||
680 | + | ||
681 | + $result = $this->db(false)->where($where)->setInc($field, $step, $lazyTime); | ||
682 | + | ||
683 | + if (true !== $result) { | ||
684 | + $this->data[$field] += $step; | ||
685 | + } | ||
686 | + | ||
687 | + return $result; | ||
688 | + } | ||
689 | + | ||
690 | + /** | ||
691 | + * 字段值(延迟)增长 | ||
692 | + * @access public | ||
693 | + * @param string $field 字段名 | ||
694 | + * @param integer $step 增长值 | ||
695 | + * @param integer $lazyTime 延时时间(s) | ||
696 | + * @return integer|true | ||
697 | + * @throws Exception | ||
698 | + */ | ||
699 | + public function setDec($field, $step = 1, $lazyTime = 0) | ||
700 | + { | ||
701 | + // 读取更新条件 | ||
702 | + $where = $this->getWhere(); | ||
703 | + | ||
704 | + $result = $this->db(false)->where($where)->setDec($field, $step, $lazyTime); | ||
705 | + | ||
706 | + if (true !== $result) { | ||
707 | + $this->data[$field] -= $step; | ||
708 | + } | ||
709 | + | ||
710 | + return $result; | ||
711 | + } | ||
712 | + | ||
713 | + /** | ||
714 | + * 获取当前的更新条件 | ||
715 | + * @access protected | ||
716 | + * @return mixed | ||
717 | + */ | ||
718 | + protected function getWhere() | ||
719 | + { | ||
720 | + // 删除条件 | ||
721 | + $pk = $this->getPk(); | ||
722 | + | ||
723 | + if (is_string($pk) && isset($this->data[$pk])) { | ||
724 | + $where[] = [$pk, '=', $this->data[$pk]]; | ||
725 | + } elseif (!empty($this->updateWhere)) { | ||
726 | + $where = $this->updateWhere; | ||
727 | + } else { | ||
728 | + $where = null; | ||
729 | + } | ||
730 | + | ||
731 | + return $where; | ||
732 | + } | ||
733 | + | ||
734 | + /** | ||
735 | + * 保存多个数据到当前数据对象 | ||
736 | + * @access public | ||
737 | + * @param array $dataSet 数据 | ||
738 | + * @param boolean $replace 是否自动识别更新和写入 | ||
739 | + * @return Collection|false | ||
740 | + * @throws \Exception | ||
741 | + */ | ||
742 | + public function saveAll($dataSet, $replace = true) | ||
743 | + { | ||
744 | + $result = []; | ||
745 | + | ||
746 | + $db = $this->db(false); | ||
747 | + $db->startTrans(); | ||
748 | + | ||
749 | + try { | ||
750 | + $pk = $this->getPk(); | ||
751 | + | ||
752 | + if (is_string($pk) && $replace) { | ||
753 | + $auto = true; | ||
754 | + } | ||
755 | + | ||
756 | + foreach ($dataSet as $key => $data) { | ||
757 | + if (!empty($auto) && isset($data[$pk])) { | ||
758 | + $result[$key] = self::update($data, [], $this->field); | ||
759 | + } else { | ||
760 | + $result[$key] = self::create($data, $this->field); | ||
761 | + } | ||
762 | + } | ||
763 | + | ||
764 | + $db->commit(); | ||
765 | + | ||
766 | + return $this->toCollection($result); | ||
767 | + } catch (\Exception $e) { | ||
768 | + $db->rollback(); | ||
769 | + throw $e; | ||
770 | + } | ||
771 | + } | ||
772 | + | ||
773 | + /** | ||
774 | + * 是否为更新数据 | ||
775 | + * @access public | ||
776 | + * @param mixed $update | ||
777 | + * @param mixed $where | ||
778 | + * @return $this | ||
779 | + */ | ||
780 | + public function isUpdate($update = true, $where = null) | ||
781 | + { | ||
782 | + if (is_bool($update)) { | ||
783 | + $this->exists = $update; | ||
784 | + | ||
785 | + if (!empty($where)) { | ||
786 | + $this->updateWhere = $where; | ||
787 | + } | ||
788 | + } else { | ||
789 | + $this->exists = true; | ||
790 | + $this->updateWhere = $update; | ||
791 | + } | ||
792 | + | ||
793 | + return $this; | ||
794 | + } | ||
795 | + | ||
796 | + /** | ||
797 | + * 删除当前的记录 | ||
798 | + * @access public | ||
799 | + * @return bool | ||
800 | + */ | ||
801 | + public function delete() | ||
802 | + { | ||
803 | + if (!$this->exists || false === $this->trigger('before_delete')) { | ||
804 | + return false; | ||
805 | + } | ||
806 | + | ||
807 | + // 读取更新条件 | ||
808 | + $where = $this->getWhere(); | ||
809 | + | ||
810 | + $db = $this->db(false); | ||
811 | + $db->startTrans(); | ||
812 | + | ||
813 | + try { | ||
814 | + // 删除当前模型数据 | ||
815 | + $db->where($where)->delete(); | ||
816 | + | ||
817 | + // 关联删除 | ||
818 | + if (!empty($this->relationWrite)) { | ||
819 | + $this->autoRelationDelete(); | ||
820 | + } | ||
821 | + | ||
822 | + $db->commit(); | ||
823 | + | ||
824 | + $this->trigger('after_delete'); | ||
825 | + | ||
826 | + $this->exists = false; | ||
827 | + | ||
828 | + return true; | ||
829 | + } catch (\Exception $e) { | ||
830 | + $db->rollback(); | ||
831 | + throw $e; | ||
832 | + } | ||
833 | + } | ||
834 | + | ||
835 | + /** | ||
836 | + * 设置自动完成的字段( 规则通过修改器定义) | ||
837 | + * @access public | ||
838 | + * @param array $fields 需要自动完成的字段 | ||
839 | + * @return $this | ||
840 | + */ | ||
841 | + public function auto($fields) | ||
842 | + { | ||
843 | + $this->auto = $fields; | ||
844 | + | ||
845 | + return $this; | ||
846 | + } | ||
847 | + | ||
848 | + /** | ||
849 | + * 写入数据 | ||
850 | + * @access public | ||
851 | + * @param array $data 数据数组 | ||
852 | + * @param array|true $field 允许字段 | ||
853 | + * @return $this | ||
854 | + */ | ||
855 | + public static function create($data = [], $field = null) | ||
856 | + { | ||
857 | + $model = new static(); | ||
858 | + | ||
859 | + if (!empty($field)) { | ||
860 | + $model->allowField($field); | ||
861 | + } | ||
862 | + | ||
863 | + $model->isUpdate(false)->save($data, []); | ||
864 | + | ||
865 | + return $model; | ||
866 | + } | ||
867 | + | ||
868 | + /** | ||
869 | + * 更新数据 | ||
870 | + * @access public | ||
871 | + * @param array $data 数据数组 | ||
872 | + * @param array $where 更新条件 | ||
873 | + * @param array|true $field 允许字段 | ||
874 | + * @return $this | ||
875 | + */ | ||
876 | + public static function update($data = [], $where = [], $field = null) | ||
877 | + { | ||
878 | + $model = new static(); | ||
879 | + | ||
880 | + if (!empty($field)) { | ||
881 | + $model->allowField($field); | ||
882 | + } | ||
883 | + | ||
884 | + $result = $model->isUpdate(true)->save($data, $where); | ||
885 | + | ||
886 | + return $model; | ||
887 | + } | ||
888 | + | ||
889 | + /** | ||
890 | + * 删除记录 | ||
891 | + * @access public | ||
892 | + * @param mixed $data 主键列表 支持闭包查询条件 | ||
893 | + * @return bool | ||
894 | + */ | ||
895 | + public static function destroy($data) | ||
896 | + { | ||
897 | + $model = new static(); | ||
898 | + | ||
899 | + $query = $model->db(); | ||
900 | + | ||
901 | + if (empty($data) && 0 !== $data) { | ||
902 | + return false; | ||
903 | + } elseif (is_array($data) && key($data) !== 0) { | ||
904 | + $query->where(self::parseWhere($data)); | ||
905 | + $data = null; | ||
906 | + } elseif ($data instanceof \Closure) { | ||
907 | + $data($query); | ||
908 | + $data = null; | ||
909 | + } | ||
910 | + | ||
911 | + $resultSet = $query->select($data); | ||
912 | + | ||
913 | + if ($resultSet) { | ||
914 | + foreach ($resultSet as $data) { | ||
915 | + $data->delete(); | ||
916 | + } | ||
917 | + } | ||
918 | + | ||
919 | + return true; | ||
920 | + } | ||
921 | + | ||
922 | + /** | ||
923 | + * 获取错误信息 | ||
924 | + * @access public | ||
925 | + * @return mixed | ||
926 | + */ | ||
927 | + public function getError() | ||
928 | + { | ||
929 | + return $this->error; | ||
930 | + } | ||
931 | + | ||
932 | + /** | ||
933 | + * 解序列化后处理 | ||
934 | + */ | ||
935 | + public function __wakeup() | ||
936 | + { | ||
937 | + $this->initialize(); | ||
938 | + } | ||
939 | + | ||
940 | + public function __debugInfo() | ||
941 | + { | ||
942 | + return [ | ||
943 | + 'data' => $this->data, | ||
944 | + 'relation' => $this->relation, | ||
945 | + ]; | ||
946 | + } | ||
947 | + | ||
948 | + /** | ||
949 | + * 修改器 设置数据对象的值 | ||
950 | + * @access public | ||
951 | + * @param string $name 名称 | ||
952 | + * @param mixed $value 值 | ||
953 | + * @return void | ||
954 | + */ | ||
955 | + public function __set($name, $value) | ||
956 | + { | ||
957 | + $this->setAttr($name, $value); | ||
958 | + } | ||
959 | + | ||
960 | + /** | ||
961 | + * 获取器 获取数据对象的值 | ||
962 | + * @access public | ||
963 | + * @param string $name 名称 | ||
964 | + * @return mixed | ||
965 | + */ | ||
966 | + public function __get($name) | ||
967 | + { | ||
968 | + return $this->getAttr($name); | ||
969 | + } | ||
970 | + | ||
971 | + /** | ||
972 | + * 检测数据对象的值 | ||
973 | + * @access public | ||
974 | + * @param string $name 名称 | ||
975 | + * @return boolean | ||
976 | + */ | ||
977 | + public function __isset($name) | ||
978 | + { | ||
979 | + try { | ||
980 | + return !is_null($this->getAttr($name)); | ||
981 | + } catch (InvalidArgumentException $e) { | ||
982 | + return false; | ||
983 | + } | ||
984 | + } | ||
985 | + | ||
986 | + /** | ||
987 | + * 销毁数据对象的值 | ||
988 | + * @access public | ||
989 | + * @param string $name 名称 | ||
990 | + * @return void | ||
991 | + */ | ||
992 | + public function __unset($name) | ||
993 | + { | ||
994 | + unset($this->data[$name], $this->relation[$name]); | ||
995 | + } | ||
996 | + | ||
997 | + // ArrayAccess | ||
998 | + public function offsetSet($name, $value) | ||
999 | + { | ||
1000 | + $this->setAttr($name, $value); | ||
1001 | + } | ||
1002 | + | ||
1003 | + public function offsetExists($name) | ||
1004 | + { | ||
1005 | + return $this->__isset($name); | ||
1006 | + } | ||
1007 | + | ||
1008 | + public function offsetUnset($name) | ||
1009 | + { | ||
1010 | + $this->__unset($name); | ||
1011 | + } | ||
1012 | + | ||
1013 | + public function offsetGet($name) | ||
1014 | + { | ||
1015 | + return $this->getAttr($name); | ||
1016 | + } | ||
1017 | + | ||
1018 | + /** | ||
1019 | + * 设置是否使用全局查询范围 | ||
1020 | + * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称) | ||
1021 | + * @access public | ||
1022 | + * @return Query | ||
1023 | + */ | ||
1024 | + public static function useGlobalScope($use) | ||
1025 | + { | ||
1026 | + $model = new static(); | ||
1027 | + | ||
1028 | + return $model->db($use); | ||
1029 | + } | ||
1030 | + | ||
1031 | + public function __call($method, $args) | ||
1032 | + { | ||
1033 | + if ('withattr' == strtolower($method)) { | ||
1034 | + return call_user_func_array([$this, 'withAttribute'], $args); | ||
1035 | + } | ||
1036 | + | ||
1037 | + return call_user_func_array([$this->db(), $method], $args); | ||
1038 | + } | ||
1039 | + | ||
1040 | + public static function __callStatic($method, $args) | ||
1041 | + { | ||
1042 | + $model = new static(); | ||
1043 | + | ||
1044 | + return call_user_func_array([$model->db(), $method], $args); | ||
1045 | + } | ||
1046 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: zhangyajun <448901948@qq.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think; | ||
13 | + | ||
14 | +use ArrayAccess; | ||
15 | +use ArrayIterator; | ||
16 | +use Countable; | ||
17 | +use IteratorAggregate; | ||
18 | +use JsonSerializable; | ||
19 | +use Traversable; | ||
20 | + | ||
21 | +abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable | ||
22 | +{ | ||
23 | + /** @var bool 是否为简洁模式 */ | ||
24 | + protected $simple = false; | ||
25 | + | ||
26 | + /** @var Collection 数据集 */ | ||
27 | + protected $items; | ||
28 | + | ||
29 | + /** @var integer 当前页 */ | ||
30 | + protected $currentPage; | ||
31 | + | ||
32 | + /** @var integer 最后一页 */ | ||
33 | + protected $lastPage; | ||
34 | + | ||
35 | + /** @var integer|null 数据总数 */ | ||
36 | + protected $total; | ||
37 | + | ||
38 | + /** @var integer 每页的数量 */ | ||
39 | + protected $listRows; | ||
40 | + | ||
41 | + /** @var bool 是否有下一页 */ | ||
42 | + protected $hasMore; | ||
43 | + | ||
44 | + /** @var array 一些配置 */ | ||
45 | + protected $options = [ | ||
46 | + 'var_page' => 'page', | ||
47 | + 'path' => '/', | ||
48 | + 'query' => [], | ||
49 | + 'fragment' => '', | ||
50 | + ]; | ||
51 | + | ||
52 | + public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) | ||
53 | + { | ||
54 | + $this->options = array_merge($this->options, $options); | ||
55 | + | ||
56 | + $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path']; | ||
57 | + | ||
58 | + $this->simple = $simple; | ||
59 | + $this->listRows = $listRows; | ||
60 | + | ||
61 | + if (!$items instanceof Collection) { | ||
62 | + $items = Collection::make($items); | ||
63 | + } | ||
64 | + | ||
65 | + if ($simple) { | ||
66 | + $this->currentPage = $this->setCurrentPage($currentPage); | ||
67 | + $this->hasMore = count($items) > ($this->listRows); | ||
68 | + $items = $items->slice(0, $this->listRows); | ||
69 | + } else { | ||
70 | + $this->total = $total; | ||
71 | + $this->lastPage = (int) ceil($total / $listRows); | ||
72 | + $this->currentPage = $this->setCurrentPage($currentPage); | ||
73 | + $this->hasMore = $this->currentPage < $this->lastPage; | ||
74 | + } | ||
75 | + $this->items = $items; | ||
76 | + } | ||
77 | + | ||
78 | + /** | ||
79 | + * @param $items | ||
80 | + * @param $listRows | ||
81 | + * @param null $currentPage | ||
82 | + * @param bool $simple | ||
83 | + * @param null $total | ||
84 | + * @param array $options | ||
85 | + * @return Paginator | ||
86 | + */ | ||
87 | + public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) | ||
88 | + { | ||
89 | + return new static($items, $listRows, $currentPage, $total, $simple, $options); | ||
90 | + } | ||
91 | + | ||
92 | + protected function setCurrentPage($currentPage) | ||
93 | + { | ||
94 | + if (!$this->simple && $currentPage > $this->lastPage) { | ||
95 | + return $this->lastPage > 0 ? $this->lastPage : 1; | ||
96 | + } | ||
97 | + | ||
98 | + return $currentPage; | ||
99 | + } | ||
100 | + | ||
101 | + /** | ||
102 | + * 获取页码对应的链接 | ||
103 | + * | ||
104 | + * @param $page | ||
105 | + * @return string | ||
106 | + */ | ||
107 | + protected function url($page) | ||
108 | + { | ||
109 | + if ($page <= 0) { | ||
110 | + $page = 1; | ||
111 | + } | ||
112 | + | ||
113 | + if (strpos($this->options['path'], '[PAGE]') === false) { | ||
114 | + $parameters = [$this->options['var_page'] => $page]; | ||
115 | + $path = $this->options['path']; | ||
116 | + } else { | ||
117 | + $parameters = []; | ||
118 | + $path = str_replace('[PAGE]', $page, $this->options['path']); | ||
119 | + } | ||
120 | + | ||
121 | + if (count($this->options['query']) > 0) { | ||
122 | + $parameters = array_merge($this->options['query'], $parameters); | ||
123 | + } | ||
124 | + | ||
125 | + $url = $path; | ||
126 | + if (!empty($parameters)) { | ||
127 | + $url .= '?' . urldecode(http_build_query($parameters, null, '&')); | ||
128 | + } | ||
129 | + | ||
130 | + return $url . $this->buildFragment(); | ||
131 | + } | ||
132 | + | ||
133 | + /** | ||
134 | + * 自动获取当前页码 | ||
135 | + * @param string $varPage | ||
136 | + * @param int $default | ||
137 | + * @return int | ||
138 | + */ | ||
139 | + public static function getCurrentPage($varPage = 'page', $default = 1) | ||
140 | + { | ||
141 | + $page = isset($_REQUEST[$varPage]) ? $_REQUEST[$varPage] : 1; | ||
142 | + | ||
143 | + if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { | ||
144 | + return $page; | ||
145 | + } | ||
146 | + | ||
147 | + return $default; | ||
148 | + } | ||
149 | + | ||
150 | + /** | ||
151 | + * 自动获取当前的path | ||
152 | + * @return string | ||
153 | + */ | ||
154 | + public static function getCurrentPath() | ||
155 | + { | ||
156 | + if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { | ||
157 | + $url = $_SERVER['HTTP_X_REWRITE_URL']; | ||
158 | + } elseif (isset($_SERVER['REQUEST_URI'])) { | ||
159 | + $url = $_SERVER['REQUEST_URI']; | ||
160 | + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { | ||
161 | + $url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); | ||
162 | + } else { | ||
163 | + $url = ''; | ||
164 | + } | ||
165 | + | ||
166 | + return strpos($url, '?') ? strstr($url, '?', true) : $url; | ||
167 | + } | ||
168 | + | ||
169 | + public function total() | ||
170 | + { | ||
171 | + if ($this->simple) { | ||
172 | + throw new \DomainException('not support total'); | ||
173 | + } | ||
174 | + | ||
175 | + return $this->total; | ||
176 | + } | ||
177 | + | ||
178 | + public function listRows() | ||
179 | + { | ||
180 | + return $this->listRows; | ||
181 | + } | ||
182 | + | ||
183 | + public function currentPage() | ||
184 | + { | ||
185 | + return $this->currentPage; | ||
186 | + } | ||
187 | + | ||
188 | + public function lastPage() | ||
189 | + { | ||
190 | + if ($this->simple) { | ||
191 | + throw new \DomainException('not support last'); | ||
192 | + } | ||
193 | + | ||
194 | + return $this->lastPage; | ||
195 | + } | ||
196 | + | ||
197 | + /** | ||
198 | + * 数据是否足够分页 | ||
199 | + * @return boolean | ||
200 | + */ | ||
201 | + public function hasPages() | ||
202 | + { | ||
203 | + return !(1 == $this->currentPage && !$this->hasMore); | ||
204 | + } | ||
205 | + | ||
206 | + /** | ||
207 | + * 创建一组分页链接 | ||
208 | + * | ||
209 | + * @param int $start | ||
210 | + * @param int $end | ||
211 | + * @return array | ||
212 | + */ | ||
213 | + public function getUrlRange($start, $end) | ||
214 | + { | ||
215 | + $urls = []; | ||
216 | + | ||
217 | + for ($page = $start; $page <= $end; $page++) { | ||
218 | + $urls[$page] = $this->url($page); | ||
219 | + } | ||
220 | + | ||
221 | + return $urls; | ||
222 | + } | ||
223 | + | ||
224 | + /** | ||
225 | + * 设置URL锚点 | ||
226 | + * | ||
227 | + * @param string|null $fragment | ||
228 | + * @return $this | ||
229 | + */ | ||
230 | + public function fragment($fragment) | ||
231 | + { | ||
232 | + $this->options['fragment'] = $fragment; | ||
233 | + | ||
234 | + return $this; | ||
235 | + } | ||
236 | + | ||
237 | + /** | ||
238 | + * 添加URL参数 | ||
239 | + * | ||
240 | + * @param array|string $key | ||
241 | + * @param string|null $value | ||
242 | + * @return $this | ||
243 | + */ | ||
244 | + public function appends($key, $value = null) | ||
245 | + { | ||
246 | + if (!is_array($key)) { | ||
247 | + $queries = [$key => $value]; | ||
248 | + } else { | ||
249 | + $queries = $key; | ||
250 | + } | ||
251 | + | ||
252 | + foreach ($queries as $k => $v) { | ||
253 | + if ($k !== $this->options['var_page']) { | ||
254 | + $this->options['query'][$k] = $v; | ||
255 | + } | ||
256 | + } | ||
257 | + | ||
258 | + return $this; | ||
259 | + } | ||
260 | + | ||
261 | + /** | ||
262 | + * 构造锚点字符串 | ||
263 | + * | ||
264 | + * @return string | ||
265 | + */ | ||
266 | + protected function buildFragment() | ||
267 | + { | ||
268 | + return $this->options['fragment'] ? '#' . $this->options['fragment'] : ''; | ||
269 | + } | ||
270 | + | ||
271 | + /** | ||
272 | + * 渲染分页html | ||
273 | + * @return mixed | ||
274 | + */ | ||
275 | + abstract public function render(); | ||
276 | + | ||
277 | + public function items() | ||
278 | + { | ||
279 | + return $this->items->all(); | ||
280 | + } | ||
281 | + | ||
282 | + public function getCollection() | ||
283 | + { | ||
284 | + return $this->items; | ||
285 | + } | ||
286 | + | ||
287 | + public function isEmpty() | ||
288 | + { | ||
289 | + return $this->items->isEmpty(); | ||
290 | + } | ||
291 | + | ||
292 | + /** | ||
293 | + * 给每个元素执行个回调 | ||
294 | + * | ||
295 | + * @param callable $callback | ||
296 | + * @return $this | ||
297 | + */ | ||
298 | + public function each(callable $callback) | ||
299 | + { | ||
300 | + foreach ($this->items as $key => $item) { | ||
301 | + $result = $callback($item, $key); | ||
302 | + | ||
303 | + if (false === $result) { | ||
304 | + break; | ||
305 | + } elseif (!is_object($item)) { | ||
306 | + $this->items[$key] = $result; | ||
307 | + } | ||
308 | + } | ||
309 | + | ||
310 | + return $this; | ||
311 | + } | ||
312 | + | ||
313 | + /** | ||
314 | + * Retrieve an external iterator | ||
315 | + * @return Traversable An instance of an object implementing <b>Iterator</b> or | ||
316 | + * <b>Traversable</b> | ||
317 | + */ | ||
318 | + public function getIterator() | ||
319 | + { | ||
320 | + return new ArrayIterator($this->items->all()); | ||
321 | + } | ||
322 | + | ||
323 | + /** | ||
324 | + * Whether a offset exists | ||
325 | + * @param mixed $offset | ||
326 | + * @return bool | ||
327 | + */ | ||
328 | + public function offsetExists($offset) | ||
329 | + { | ||
330 | + return $this->items->offsetExists($offset); | ||
331 | + } | ||
332 | + | ||
333 | + /** | ||
334 | + * Offset to retrieve | ||
335 | + * @param mixed $offset | ||
336 | + * @return mixed | ||
337 | + */ | ||
338 | + public function offsetGet($offset) | ||
339 | + { | ||
340 | + return $this->items->offsetGet($offset); | ||
341 | + } | ||
342 | + | ||
343 | + /** | ||
344 | + * Offset to set | ||
345 | + * @param mixed $offset | ||
346 | + * @param mixed $value | ||
347 | + */ | ||
348 | + public function offsetSet($offset, $value) | ||
349 | + { | ||
350 | + $this->items->offsetSet($offset, $value); | ||
351 | + } | ||
352 | + | ||
353 | + /** | ||
354 | + * Offset to unset | ||
355 | + * @param mixed $offset | ||
356 | + * @return void | ||
357 | + * @since 5.0.0 | ||
358 | + */ | ||
359 | + public function offsetUnset($offset) | ||
360 | + { | ||
361 | + $this->items->offsetUnset($offset); | ||
362 | + } | ||
363 | + | ||
364 | + /** | ||
365 | + * Count elements of an object | ||
366 | + */ | ||
367 | + public function count() | ||
368 | + { | ||
369 | + return $this->items->count(); | ||
370 | + } | ||
371 | + | ||
372 | + public function __toString() | ||
373 | + { | ||
374 | + return (string) $this->render(); | ||
375 | + } | ||
376 | + | ||
377 | + public function toArray() | ||
378 | + { | ||
379 | + try { | ||
380 | + $total = $this->total(); | ||
381 | + } catch (\DomainException $e) { | ||
382 | + $total = null; | ||
383 | + } | ||
384 | + | ||
385 | + return [ | ||
386 | + 'total' => $total, | ||
387 | + 'per_page' => $this->listRows(), | ||
388 | + 'current_page' => $this->currentPage(), | ||
389 | + 'last_page' => $this->lastPage, | ||
390 | + 'data' => $this->items->toArray(), | ||
391 | + ]; | ||
392 | + } | ||
393 | + | ||
394 | + /** | ||
395 | + * Specify data which should be serialized to JSON | ||
396 | + */ | ||
397 | + public function jsonSerialize() | ||
398 | + { | ||
399 | + return $this->toArray(); | ||
400 | + } | ||
401 | + | ||
402 | + public function __call($name, $arguments) | ||
403 | + { | ||
404 | + return call_user_func_array([$this->getCollection(), $name], $arguments); | ||
405 | + } | ||
406 | + | ||
407 | +} |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: liu21st <liu21st@gmail.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | +namespace think; | ||
12 | + | ||
13 | +Db::setConfig([ | ||
14 | + // 数据库类型 | ||
15 | + 'type' => '', | ||
16 | + // 服务器地址 | ||
17 | + 'hostname' => '', | ||
18 | + // 数据库名 | ||
19 | + 'database' => '', | ||
20 | + // 用户名 | ||
21 | + 'username' => '', | ||
22 | + // 密码 | ||
23 | + 'password' => '', | ||
24 | + // 端口 | ||
25 | + 'hostport' => '', | ||
26 | + // 连接dsn | ||
27 | + 'dsn' => '', | ||
28 | + // 数据库连接参数 | ||
29 | + 'params' => [], | ||
30 | + // 数据库编码默认采用utf8 | ||
31 | + 'charset' => 'utf8', | ||
32 | + // 数据库表前缀 | ||
33 | + 'prefix' => '', | ||
34 | + // 数据库调试模式 | ||
35 | + 'debug' => false, | ||
36 | + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) | ||
37 | + 'deploy' => 0, | ||
38 | + // 数据库读写是否分离 主从式有效 | ||
39 | + 'rw_separate' => false, | ||
40 | + // 读写分离后 主服务器数量 | ||
41 | + 'master_num' => 1, | ||
42 | + // 指定从服务器序号 | ||
43 | + 'slave_no' => '', | ||
44 | + // 是否严格检查字段是否存在 | ||
45 | + 'fields_strict' => true, | ||
46 | + // 数据集返回类型 | ||
47 | + 'resultset_type' => '', | ||
48 | + // 自动写入时间戳字段 | ||
49 | + 'auto_timestamp' => false, | ||
50 | + // 时间字段取出后的默认时间格式 | ||
51 | + 'datetime_format' => 'Y-m-d H:i:s', | ||
52 | + // 是否需要进行SQL性能分析 | ||
53 | + 'sql_explain' => false, | ||
54 | + // Builder类 | ||
55 | + 'builder' => '', | ||
56 | + // Query类 | ||
57 | + 'query' => '\\think\\db\\Query', | ||
58 | + // 是否需要断线重连 | ||
59 | + 'break_reconnect' => false, | ||
60 | + // 默认分页设置 | ||
61 | + 'paginate' => [ | ||
62 | + 'type' => 'bootstrap', | ||
63 | + 'var_page' => 'page', | ||
64 | + 'list_rows' => 15, | ||
65 | + ] | ||
66 | +]); |
1 | +<?php | ||
2 | +// +---------------------------------------------------------------------- | ||
3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK ] | ||
4 | +// +---------------------------------------------------------------------- | ||
5 | +// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. | ||
6 | +// +---------------------------------------------------------------------- | ||
7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) | ||
8 | +// +---------------------------------------------------------------------- | ||
9 | +// | Author: liu21st <liu21st@gmail.com> | ||
10 | +// +---------------------------------------------------------------------- | ||
11 | + | ||
12 | +namespace think\db; | ||
13 | + | ||
14 | +use PDO; | ||
15 | +use think\Exception; | ||
16 | + | ||
17 | +abstract class Builder | ||
18 | +{ | ||
19 | + // connection对象实例 | ||
20 | + protected $connection; | ||
21 | + | ||
22 | + // 查询表达式映射 | ||
23 | + protected $exp = ['EQ' => '=', 'NEQ' => '<>', 'GT' => '>', 'EGT' => '>=', 'LT' => '<', 'ELT' => '<=', 'NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME']; | ||
24 | + | ||
25 | + // 查询表达式解析 | ||
26 | + protected $parser = [ | ||
27 | + 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='], | ||
28 | + 'parseLike' => ['LIKE', 'NOT LIKE'], | ||
29 | + 'parseBetween' => ['NOT BETWEEN', 'BETWEEN'], | ||
30 | + 'parseIn' => ['NOT IN', 'IN'], | ||
31 | + 'parseExp' => ['EXP'], | ||
32 | + 'parseNull' => ['NOT NULL', 'NULL'], | ||
33 | + 'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'], | ||
34 | + 'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'], | ||
35 | + 'parseExists' => ['NOT EXISTS', 'EXISTS'], | ||
36 | + 'parseColumn' => ['COLUMN'], | ||
37 | + ]; | ||
38 | + | ||
39 | + // SQL表达式 | ||
40 | + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%'; | ||
41 | + | ||
42 | + protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; | ||
43 | + | ||
44 | + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; | ||
45 | + | ||
46 | + protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; | ||
47 | + | ||
48 | + protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; | ||
49 | + | ||
50 | + /** | ||
51 | + * 架构函数 | ||
52 | + * @access public | ||
53 | + * @param Connection $connection 数据库连接对象实例 | ||
54 | + */ | ||
55 | + public function __construct(Connection $connection) | ||
56 | + { | ||
57 | + $this->connection = $connection; | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * 获取当前的连接对象实例 | ||
62 | + * @access public | ||
63 | + * @return Connection | ||
64 | + */ | ||
65 | + public function getConnection() | ||
66 | + { | ||
67 | + return $this->connection; | ||
68 | + } | ||
69 | + | ||
70 | + /** | ||
71 | + * 注册查询表达式解析 | ||
72 | + * @access public | ||
73 | + * @param string $name 解析方法 | ||
74 | + * @param array $parser 匹配表达式数据 | ||
75 | + * @return $this | ||
76 | + */ | ||
77 | + public function bindParser($name, $parser) | ||
78 | + { | ||
79 | + $this->parser[$name] = $parser; | ||
80 | + return $this; | ||
81 | + } | ||
82 | + | ||
83 | + /** | ||
84 | + * 数据分析 | ||
85 | + * @access protected | ||
86 | + * @param Query $query 查询对象 | ||
87 | + * @param array $data 数据 | ||
88 | + * @param array $fields 字段信息 | ||
89 | + * @param array $bind 参数绑定 | ||
90 | + * @return array | ||
91 | + */ | ||
92 | + protected function parseData(Query $query, $data = [], $fields = [], $bind = []) | ||
93 | + { | ||
94 | + if (empty($data)) { | ||
95 | + return []; | ||
96 | + } | ||
97 | + | ||
98 | + $options = $query->getOptions(); | ||
99 | + | ||
100 | + // 获取绑定信息 | ||
101 | + if (empty($bind)) { | ||
102 | + $bind = $this->connection->getFieldsBind($options['table']); | ||
103 | + } | ||
104 | + | ||
105 | + if (empty($fields)) { | ||
106 | + if ('*' == $options['field']) { | ||
107 | + $fields = array_keys($bind); | ||
108 | + } else { | ||
109 | + $fields = $options['field']; | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + $result = []; | ||
114 | + | ||
115 | + foreach ($data as $key => $val) { | ||
116 | + if ('*' != $options['field'] && !in_array($key, $fields, true)) { | ||
117 | + continue; | ||
118 | + } | ||
119 | + | ||
120 | + $item = $this->parseKey($query, $key, true); | ||
121 | + | ||
122 | + if ($val instanceof Expression) { | ||
123 | + $result[$item] = $val->getValue(); | ||
124 | + continue; | ||
125 | + } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) { | ||
126 | + $val = json_encode($val, JSON_UNESCAPED_UNICODE); | ||
127 | + } elseif (is_object($val) && method_exists($val, '__toString')) { | ||
128 | + // 对象数据写入 | ||
129 | + $val = $val->__toString(); | ||
130 | + } | ||
131 | + | ||
132 | + if (false !== strpos($key, '->')) { | ||
133 | + list($key, $name) = explode('->', $key); | ||
134 | + $item = $this->parseKey($query, $key); | ||
135 | + $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')'; | ||
136 | + } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) { | ||
137 | + if ($options['strict']) { | ||
138 | + throw new Exception('fields not exists:[' . $key . ']'); | ||
139 | + } | ||
140 | + } elseif (is_null($val)) { | ||
141 | + $result[$item] = 'NULL'; | ||
142 | + } elseif (is_array($val) && !empty($val)) { | ||
143 | + switch (strtoupper($val[0])) { | ||
144 | + case 'INC': | ||
145 | + $result[$item] = $item . ' + ' . floatval($val[1]); | ||
146 | + break; | ||
147 | + case 'DEC': | ||
148 | + $result[$item] = $item . ' - ' . floatval($val[1]); | ||
149 | + break; | ||
150 | + case 'EXP': | ||
151 | + throw new Exception('not support data:[' . $val[0] . ']'); | ||
152 | + } | ||
153 | + } elseif (is_scalar($val)) { | ||
154 | + // 过滤非标量数据 | ||
155 | + $result[$item] = $this->parseDataBind($query, $key, $val, $bind); | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | + return $result; | ||
160 | + } | ||
161 | + | ||
162 | + /** | ||
163 | + * 数据绑定处理 | ||
164 | + * @access protected | ||
165 | + * @param Query $query 查询对象 | ||
166 | + * @param string $key 字段名 | ||
167 | + * @param mixed $data 数据 | ||
168 | + * @param array $bind 绑定数据 | ||
169 | + * @return string | ||
170 | + */ | ||
171 | + protected function parseDataBind(Query $query, $key, $data, $bind = []) | ||
172 | + { | ||
173 | + if ($data instanceof Expression) { | ||
174 | + return $data->getValue(); | ||
175 | + } | ||
176 | + | ||
177 | + $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); | ||
178 | + | ||
179 | + return '?'; | ||
180 | + } | ||
181 | + | ||
182 | + /** | ||
183 | + * 字段名分析 | ||
184 | + * @access public | ||
185 | + * @param Query $query 查询对象 | ||
186 | + * @param mixed $key 字段名 | ||
187 | + * @param bool $strict 严格检测 | ||
188 | + * @return string | ||
189 | + */ | ||
190 | + public function parseKey(Query $query, $key, $strict = false) | ||
191 | + { | ||
192 | + return $key instanceof Expression ? $key->getValue() : $key; | ||
193 | + } | ||
194 | + | ||
195 | + /** | ||
196 | + * field分析 | ||
197 | + * @access protected | ||
198 | + * @param Query $query 查询对象 | ||
199 | + * @param mixed $fields | ||
200 | + * @return string | ||
201 | + */ | ||
202 | + protected function parseField(Query $query, $fields) | ||
203 | + { | ||
204 | + if ('*' == $fields || empty($fields)) { | ||
205 | + $fieldsStr = '*'; | ||
206 | + } elseif (is_array($fields)) { | ||
207 | + // 支持 'field1'=>'field2' 这样的字段别名定义 | ||
208 | + $array = []; | ||
209 | + | ||
210 | + foreach ($fields as $key => $field) { | ||
211 | + if (!is_numeric($key)) { | ||
212 | + $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true); | ||
213 | + } else { | ||
214 | + $array[] = $this->parseKey($query, $field); | ||
215 | + } | ||
216 | + } | ||
217 | + | ||
218 | + $fieldsStr = implode(',', $array); | ||
219 | + } | ||
220 | + | ||
221 | + return $fieldsStr; | ||
222 | + } | ||
223 | + | ||
224 | + /** | ||
225 | + * table分析 | ||
226 | + * @access protected | ||
227 | + * @param Query $query 查询对象 | ||
228 | + * @param mixed $tables | ||
229 | + * @return string | ||
230 | + */ | ||
231 | + protected function parseTable(Query $query, $tables) | ||
232 | + { | ||
233 | + $item = []; | ||
234 | + $options = $query->getOptions(); | ||
235 | + | ||
236 | + foreach ((array) $tables as $key => $table) { | ||
237 | + if (!is_numeric($key)) { | ||
238 | + $key = $this->connection->parseSqlTable($key); | ||
239 | + $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table); | ||
240 | + } else { | ||
241 | + $table = $this->connection->parseSqlTable($table); | ||
242 | + | ||
243 | + if (isset($options['alias'][$table])) { | ||
244 | + $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]); | ||
245 | + } else { | ||
246 | + $item[] = $this->parseKey($query, $table); | ||
247 | + } | ||
248 | + } | ||
249 | + } | ||
250 | + | ||
251 | + return implode(',', $item); | ||
252 | + } | ||
253 | + | ||
254 | + /** | ||
255 | + * where分析 | ||
256 | + * @access protected | ||
257 | + * @param Query $query 查询对象 | ||
258 | + * @param mixed $where 查询条件 | ||
259 | + * @return string | ||
260 | + */ | ||
261 | + protected function parseWhere(Query $query, $where) | ||
262 | + { | ||
263 | + $options = $query->getOptions(); | ||
264 | + $whereStr = $this->buildWhere($query, $where); | ||
265 | + | ||
266 | + if (!empty($options['soft_delete'])) { | ||
267 | + // 附加软删除条件 | ||
268 | + list($field, $condition) = $options['soft_delete']; | ||
269 | + | ||
270 | + $binds = $this->connection->getFieldsBind($options['table']); | ||
271 | + $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; | ||
272 | + $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds); | ||
273 | + } | ||
274 | + | ||
275 | + return empty($whereStr) ? '' : ' WHERE ' . $whereStr; | ||
276 | + } | ||
277 | + | ||
278 | + /** | ||
279 | + * 生成查询条件SQL | ||
280 | + * @access public | ||
281 | + * @param Query $query 查询对象 | ||
282 | + * @param mixed $where | ||
283 | + * @param array $options | ||
284 | + * @return string | ||
285 | + */ | ||
286 | + public function buildWhere(Query $query, $where) | ||
287 | + { | ||
288 | + if (empty($where)) { | ||
289 | + $where = []; | ||
290 | + } | ||
291 | + | ||
292 | + $whereStr = ''; | ||
293 | + $binds = $this->connection->getFieldsBind($query->getOptions('table')); | ||
294 | + | ||
295 | + foreach ($where as $logic => $val) { | ||
296 | + $str = []; | ||
297 | + | ||
298 | + foreach ($val as $value) { | ||
299 | + if ($value instanceof Expression) { | ||
300 | + $str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; | ||
301 | + continue; | ||
302 | + } | ||
303 | + | ||
304 | + if (is_array($value)) { | ||
305 | + if (key($value) !== 0) { | ||
306 | + throw new Exception('where express error:' . var_export($value, true)); | ||
307 | + } | ||
308 | + $field = array_shift($value); | ||
309 | + } elseif (!($value instanceof \Closure)) { | ||
310 | + throw new Exception('where express error:' . var_export($value, true)); | ||
311 | + } | ||
312 | + | ||
313 | + if ($value instanceof \Closure) { | ||
314 | + // 使用闭包查询 | ||
315 | + $newQuery = $query->newQuery()->setConnection($this->connection); | ||
316 | + $value($newQuery); | ||
317 | + $whereClause = $this->buildWhere($query, $newQuery->getOptions('where')); | ||
318 | + | ||
319 | + if (!empty($whereClause)) { | ||
320 | + $str[] = ' ' . $logic . ' ( ' . $whereClause . ' )'; | ||
321 | + } | ||
322 | + } elseif (is_array($field)) { | ||
323 | + array_unshift($value, $field); | ||
324 | + $str2 = []; | ||
325 | + foreach ($value as $item) { | ||
326 | + $str2[] = $this->parseWhereItem($query, array_shift($item), $item, $logic, $binds); | ||
327 | + } | ||
328 | + | ||
329 | + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $str2) . ' )'; | ||
330 | + } elseif (strpos($field, '|')) { | ||
331 | + // 不同字段使用相同查询条件(OR) | ||
332 | + $array = explode('|', $field); | ||
333 | + $item = []; | ||
334 | + | ||
335 | + foreach ($array as $k) { | ||
336 | + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); | ||
337 | + } | ||
338 | + | ||
339 | + $str[] = ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )'; | ||
340 | + } elseif (strpos($field, '&')) { | ||
341 | + // 不同字段使用相同查询条件(AND) | ||
342 | + $array = explode('&', $field); | ||
343 | + $item = []; | ||
344 | + | ||
345 | + foreach ($array as $k) { | ||
346 | + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); | ||
347 | + } | ||
348 | + | ||
349 | + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )'; | ||
350 | + } else { | ||
351 | + // 对字段使用表达式查询 | ||
352 | + $field = is_string($field) ? $field : ''; | ||
353 | + $str[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $logic, $binds); | ||
354 | + } | ||
355 | + } | ||
356 | + | ||
357 | + $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str); | ||
358 | + } | ||
359 | + | ||
360 | + return $whereStr; | ||
361 | + } | ||
362 | + | ||
363 | + // where子单元分析 | ||
364 | + protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = []) | ||
365 | + { | ||
366 | + // 字段分析 | ||
367 | + $key = $field ? $this->parseKey($query, $field, true) : ''; | ||
368 | + | ||
369 | + // 查询规则和条件 | ||
370 | + if (!is_array($val)) { | ||
371 | + $val = is_null($val) ? ['NULL', ''] : ['=', $val]; | ||
372 | + } | ||
373 | + | ||
374 | + list($exp, $value) = $val; | ||
375 | + | ||
376 | + // 对一个字段使用多个查询条件 | ||
377 | + if (is_array($exp)) { | ||
378 | + $item = array_pop($val); | ||
379 | + | ||
380 | + // 传入 or 或者 and | ||
381 | + if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) { | ||
382 | + $rule = $item; | ||
383 | + } else { | ||
384 | + array_push($val, $item); | ||
385 | + } | ||
386 | + | ||
387 | + foreach ($val as $k => $item) { | ||
388 | + $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds); | ||
389 | + } | ||
390 | + | ||
391 | + return '( ' . implode(' ' . $rule . ' ', $str) . ' )'; | ||
392 | + } | ||
393 | + | ||
394 | + // 检测操作符 | ||
395 | + $exp = strtoupper($exp); | ||
396 | + if (isset($this->exp[$exp])) { | ||
397 | + $exp = $this->exp[$exp]; | ||
398 | + } | ||
399 | + | ||
400 | + if ($value instanceof Expression) { | ||
401 | + | ||
402 | + } elseif (is_object($value) && method_exists($value, '__toString')) { | ||
403 | + // 对象数据写入 | ||
404 | + $value = $value->__toString(); | ||
405 | + } | ||
406 | + | ||
407 | + if (strpos($field, '->')) { | ||
408 | + $jsonType = $query->getJsonFieldType($field); | ||
409 | + $bindType = $this->connection->getFieldBindType($jsonType); | ||
410 | + } else { | ||
411 | + $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; | ||
412 | + } | ||
413 | + | ||
414 | + if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { | ||
415 | + $query->bind($value, $bindType); | ||
416 | + $value = '?'; | ||
417 | + } | ||
418 | + | ||
419 | + // 解析查询表达式 | ||
420 | + foreach ($this->parser as $fun => $parse) { | ||
421 | + if (in_array($exp, $parse)) { | ||
422 | + $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND'); | ||
423 | + break; | ||
424 | + } | ||
425 | + } | ||
426 | + | ||
427 | + if (!isset($whereStr)) { | ||
428 | + throw new Exception('where express error:' . $exp); | ||
429 | + } | ||
430 | + | ||
431 | + return $whereStr; | ||
432 | + } | ||
433 | + | ||
434 | + /** | ||
435 | + * 模糊查询 | ||
436 | + * @access protected | ||
437 | + * @param Query $query 查询对象 | ||
438 | + * @param string $key | ||
439 | + * @param string $exp | ||
440 | + * @param mixed $value | ||
441 | + * @param string $field | ||
442 | + * @param integer $bindType | ||
443 | + * @param string $logic | ||
444 | + * @return string | ||
445 | + */ | ||
446 | + protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic) | ||
447 | + { | ||
448 | + // 模糊匹配 | ||
449 | + if (is_array($value)) { | ||
450 | + foreach ($value as $item) { | ||
451 | + $bind[] = [$item, $bindType]; | ||
452 | + $array[] = $key . ' ' . $exp . ' ?'; | ||
453 | + } | ||
454 | + | ||
455 | + $query->bind($bind); | ||
456 | + | ||
457 | + $whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; | ||
458 | + } else { | ||
459 | + $whereStr = $key . ' ' . $exp . ' ' . $value; | ||
460 | + } | ||
461 | + | ||
462 | + return $whereStr; | ||
463 | + } | ||
464 | + | ||
465 | + /** | ||
466 | + * 表达式查询 | ||
467 | + * @access protected | ||
468 | + * @param Query $query 查询对象 | ||
469 | + * @param string $key | ||
470 | + * @param string $exp | ||
471 | + * @param Expression $value | ||
472 | + * @param string $field | ||
473 | + * @param integer $bindType | ||
474 | + * @return string | ||
475 | + */ | ||
476 | + protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType) | ||
477 | + { | ||
478 | + // 表达式查询 | ||
479 | + return '( ' . $key . ' ' . $value->getValue() . ' )'; | ||
480 | + } | ||
481 | + | ||
482 | + /** | ||
483 | + * 表达式查询 | ||
484 | + * @access protected | ||
485 | + * @param Query $query 查询对象 | ||
486 | + * @param string $key | ||
487 | + * @param string $exp | ||
488 | + * @param array $value | ||
489 | + * @param string $field | ||
490 | + * @param integer $bindType | ||
491 | + * @return string | ||
492 | + */ | ||
493 | + protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType) | ||
494 | + { | ||
495 | + // 字段比较查询 | ||
496 | + list($op, $field2) = $value; | ||
497 | + | ||
498 | + if (!in_array($op, ['=', '<>', '>', '>=', '<', '<='])) { | ||
499 | + throw new Exception('where express error:' . var_export($value, true)); | ||
500 | + } | ||
501 | + | ||
502 | + return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field2, true) . ' )'; | ||
503 | + } | ||
504 | + | ||
505 | + /** | ||
506 | + * Null查询 | ||
507 | + * @access protected | ||
508 | + * @param Query $query 查询对象 | ||
509 | + * @param string $key | ||
510 | + * @param string $exp | ||
511 | + * @param mixed $value | ||
512 | + * @param string $field | ||
513 | + * @param integer $bindType | ||
514 | + * @return string | ||
515 | + */ | ||
516 | + protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType) | ||
517 | + { | ||
518 | + // NULL 查询 | ||
519 | + return $key . ' IS ' . $exp; | ||
520 | + } | ||
521 | + | ||
522 | + /** | ||
523 | + * 范围查询 | ||
524 | + * @access protected | ||
525 | + * @param Query $query 查询对象 | ||
526 | + * @param string $key | ||
527 | + * @param string $exp | ||
528 | + * @param mixed $value | ||
529 | + * @param string $field | ||
530 | + * @param integer $bindType | ||
531 | + * @return string | ||
532 | + */ | ||
533 | + protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType) | ||
534 | + { | ||
535 | + // BETWEEN 查询 | ||
536 | + $data = is_array($value) ? $value : explode(',', $value); | ||
537 | + | ||
538 | + $bind[] = [$data[0], $bindType]; | ||
539 | + $bind[] = [$data[1], $bindType]; | ||
540 | + | ||
541 | + $query->bind($bind); | ||
542 | + | ||
543 | + return $key . ' ' . $exp . ' ? AND ? '; | ||
544 | + } | ||
545 | + | ||
546 | + /** | ||
547 | + * Exists查询 | ||
548 | + * @access protected | ||
549 | + * @param Query $query 查询对象 | ||
550 | + * @param string $key | ||
551 | + * @param string $exp | ||
552 | + * @param mixed $value | ||
553 | + * @param string $field | ||
554 | + * @param integer $bindType | ||
555 | + * @return string | ||
556 | + */ | ||
557 | + protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType) | ||
558 | + { | ||
559 | + // EXISTS 查询 | ||
560 | + if ($value instanceof \Closure) { | ||
561 | + $value = $this->parseClosure($query, $value, false); | ||
562 | + } elseif ($value instanceof Expression) { | ||
563 | + $value = $value->getValue(); | ||
564 | + } else { | ||
565 | + throw new Exception('where express error:' . $value); | ||
566 | + } | ||
567 | + | ||
568 | + return $exp . ' (' . $value . ')'; | ||
569 | + } | ||
570 | + | ||
571 | + /** | ||
572 | + * 时间比较查询 | ||
573 | + * @access protected | ||
574 | + * @param Query $query 查询对象 | ||
575 | + * @param string $key | ||
576 | + * @param string $exp | ||
577 | + * @param mixed $value | ||
578 | + * @param string $field | ||
579 | + * @param integer $bindType | ||
580 | + * @return string | ||
581 | + */ | ||
582 | + protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType) | ||
583 | + { | ||
584 | + return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType); | ||
585 | + } | ||
586 | + | ||
587 | + /** | ||
588 | + * 大小比较查询 | ||
589 | + * @access protected | ||
590 | + * @param Query $query 查询对象 | ||
591 | + * @param string $key | ||
592 | + * @param string $exp | ||
593 | + * @param mixed $value | ||
594 | + * @param string $field | ||
595 | + * @param integer $bindType | ||
596 | + * @return string | ||
597 | + */ | ||
598 | + protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType) | ||
599 | + { | ||
600 | + if (is_array($value)) { | ||
601 | + throw new Exception('where express error:' . $exp . var_export($value, true)); | ||
602 | + } | ||
603 | + | ||
604 | + // 比较运算 | ||
605 | + if ($value instanceof \Closure) { | ||
606 | + $value = $this->parseClosure($query, $value); | ||
607 | + } | ||
608 | + | ||
609 | + return $key . ' ' . $exp . ' ' . $value; | ||
610 | + } | ||
611 | + | ||
612 | + /** | ||
613 | + * 时间范围查询 | ||
614 | + * @access protected | ||
615 | + * @param Query $query 查询对象 | ||
616 | + * @param string $key | ||
617 | + * @param string $exp | ||
618 | + * @param mixed $value | ||
619 | + * @param string $field | ||
620 | + * @param integer $bindType | ||
621 | + * @return string | ||
622 | + */ | ||
623 | + protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType) | ||
624 | + { | ||
625 | + if (is_string($value)) { | ||
626 | + $value = explode(',', $value); | ||
627 | + } | ||
628 | + | ||
629 | + return $key . ' ' . substr($exp, 0, -4) | ||
630 | + . $this->parseDateTime($query, $value[0], $field, $bindType) | ||
631 | + . ' AND ' | ||
632 | + . $this->parseDateTime($query, $value[1], $field, $bindType); | ||
633 | + | ||
634 | + } | ||
635 | + | ||
636 | + /** | ||
637 | + * IN查询 | ||
638 | + * @access protected | ||
639 | + * @param Query $query 查询对象 | ||
640 | + * @param string $key | ||
641 | + * @param string $exp | ||
642 | + * @param mixed $value | ||
643 | + * @param string $field | ||
644 | + * @param integer $bindType | ||
645 | + * @return string | ||
646 | + */ | ||
647 | + protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType) | ||
648 | + { | ||
649 | + // IN 查询 | ||
650 | + if ($value instanceof \Closure) { | ||
651 | + $value = $this->parseClosure($query, $value, false); | ||
652 | + } else { | ||
653 | + $value = array_unique(is_array($value) ? $value : explode(',', $value)); | ||
654 | + | ||
655 | + $bind = []; | ||
656 | + $array = []; | ||
657 | + | ||
658 | + foreach ($value as $k => $v) { | ||
659 | + $bind[] = [$v, $bindType]; | ||
660 | + $array[] = '?'; | ||
661 | + } | ||
662 | + | ||
663 | + $zone = implode(',', $array); | ||
664 | + $query->bind($bind); | ||
665 | + | ||
666 | + $value = empty($zone) ? "''" : $zone; | ||
667 | + } | ||
668 | + | ||
669 | + return $key . ' ' . $exp . ' (' . $value . ')'; | ||
670 | + } | ||
671 | + | ||
672 | + /** | ||
673 | + * 闭包子查询 | ||
674 | + * @access protected | ||
675 | + * @param Query $query 查询对象 | ||
676 | + * @param \Closure $call | ||
677 | + * @param bool $show | ||
678 | + * @return string | ||
679 | + */ | ||
680 | + protected function parseClosure(Query $query, $call, $show = true) | ||
681 | + { | ||
682 | + $newQuery = $query->newQuery()->setConnection($this->connection); | ||
683 | + $call($newQuery); | ||
684 | + | ||
685 | + return $newQuery->buildSql($show); | ||
686 | + } | ||
687 | + | ||
688 | + /** | ||
689 | + * 日期时间条件解析 | ||
690 | + * @access protected | ||
691 | + * @param Query $query 查询对象 | ||
692 | + * @param string $value | ||
693 | + * @param string $key | ||
694 | + * @param integer $bindType | ||
695 | + * @return string | ||
696 | + */ | ||
697 | + protected function parseDateTime(Query $query, $value, $key, $bindType = null) | ||
698 | + { | ||
699 | + $options = $query->getOptions(); | ||
700 | + | ||
701 | + // 获取时间字段类型 | ||
702 | + if (strpos($key, '.')) { | ||
703 | + list($table, $key) = explode('.', $key); | ||
704 | + | ||
705 | + if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) { | ||
706 | + $table = $pos; | ||
707 | + } | ||
708 | + } else { | ||
709 | + $table = $options['table']; | ||
710 | + } | ||
711 | + | ||
712 | + $type = $this->connection->getTableInfo($table, 'type'); | ||
713 | + | ||
714 | + if (isset($type[$key])) { | ||
715 | + $info = $type[$key]; | ||
716 | + } | ||
717 | + | ||
718 | + if (isset($info)) { | ||
719 | + if (is_string($value)) { | ||
720 | + $value = strtotime($value) ?: $value; | ||
721 | + } | ||
722 | + | ||
723 | + if (preg_match('/(datetime|timestamp)/is', $info)) { | ||
724 | + // 日期及时间戳类型 | ||
725 | + $value = date('Y-m-d H:i:s', $value); | ||
726 | + } elseif (preg_match('/(date)/is', $info)) { | ||
727 | + // 日期及时间戳类型 | ||
728 | + $value = date('Y-m-d', $value); | ||
729 | + } | ||
730 | + } | ||
731 | + | ||
732 | + $query->bind($value, $bindType); | ||
733 | + | ||
734 | + return '?'; | ||
735 | + } | ||
736 | + | ||
737 | + /** | ||
738 | + * limit分析 | ||
739 | + * @access protected | ||
740 | + * @param Query $query 查询对象 | ||
741 | + * @param mixed $limit | ||
742 | + * @return string | ||
743 | + */ | ||
744 | + protected function parseLimit(Query $query, $limit) | ||
745 | + { | ||
746 | + return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : ''; | ||
747 | + } | ||
748 | + | ||
749 | + /** | ||
750 | + * join分析 | ||
751 | + * @access protected | ||
752 | + * @param Query $query 查询对象 | ||
753 | + * @param array $join | ||
754 | + * @return string | ||
755 | + */ | ||
756 | + protected function parseJoin(Query $query, $join) | ||
757 | + { | ||
758 | + $joinStr = ''; | ||
759 | + | ||
760 | + if (!empty($join)) { | ||
761 | + foreach ($join as $item) { | ||
762 | + list($table, $type, $on) = $item; | ||
763 | + | ||
764 | + $condition = []; | ||
765 | + | ||
766 | + foreach ((array) $on as $val) { | ||
767 | + if ($val instanceof Expression) { | ||
768 | + $condition[] = $val->getValue(); | ||
769 | + } elseif (strpos($val, '=')) { | ||
770 | + list($val1, $val2) = explode('=', $val, 2); | ||
771 | + $condition[] = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2); | ||
772 | + } else { | ||
773 | + $condition[] = $val; | ||
774 | + } | ||
775 | + } | ||
776 | + | ||
777 | + $table = $this->parseTable($query, $table); | ||
778 | + | ||
779 | + $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition); | ||
780 | + } | ||
781 | + } | ||
782 | + | ||
783 | + return $joinStr; | ||
784 | + } | ||
785 | + | ||
786 | + /** | ||
787 | + * order分析 | ||
788 | + * @access protected | ||
789 | + * @param Query $query 查询对象 | ||
790 | + * @param mixed $order | ||
791 | + * @return string | ||
792 | + */ | ||
793 | + protected function parseOrder(Query $query, $order) | ||
794 | + { | ||
795 | + foreach ($order as $key => $val) { | ||
796 | + if ($val instanceof Expression) { | ||
797 | + $array[] = $val->getValue(); | ||
798 | + } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) { | ||
799 | + $array[] = $this->parseOrderField($query, $key, $val); | ||
800 | + } elseif ('[rand]' == $val) { | ||
801 | + $array[] = $this->parseRand($query); | ||
802 | + } elseif (is_string($val)) { | ||
803 | + if (is_numeric($key)) { | ||
804 | + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); | ||
805 | + } else { | ||
806 | + $sort = $val; | ||
807 | + } | ||
808 | + | ||
809 | + if (preg_match('/^[\w\.]+$/', $key)) { | ||
810 | + $sort = strtoupper($sort); | ||
811 | + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; | ||
812 | + $array[] = $this->parseKey($query, $key, true) . $sort; | ||
813 | + } else { | ||
814 | + throw new Exception('order express error:' . $key); | ||
815 | + } | ||
816 | + } | ||
817 | + } | ||
818 | + | ||
819 | + return empty($array) ? '' : ' ORDER BY ' . implode(',', $array); | ||
820 | + } | ||
821 | + | ||
822 | + /** | ||
823 | + * orderField分析 | ||
824 | + * @access protected | ||
825 | + * @param Query $query 查询对象 | ||
826 | + * @param mixed $key | ||
827 | + * @param array $val | ||
828 | + * @return string | ||
829 | + */ | ||
830 | + protected function parseOrderField($query, $key, $val) | ||
831 | + { | ||
832 | + if (isset($val['sort'])) { | ||
833 | + $sort = $val['sort']; | ||
834 | + unset($val['sort']); | ||
835 | + } else { | ||
836 | + $sort = ''; | ||
837 | + } | ||
838 | + | ||
839 | + $sort = strtoupper($sort); | ||
840 | + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; | ||
841 | + | ||
842 | + $options = $query->getOptions(); | ||
843 | + $bind = $this->connection->getFieldsBind($options['table']); | ||
844 | + | ||
845 | + foreach ($val as $k => $item) { | ||
846 | + $val[$k] = $this->parseDataBind($query, $key, $item, $bind); | ||
847 | + } | ||
848 | + | ||
849 | + return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort; | ||
850 | + } | ||
851 | + | ||
852 | + /** | ||
853 | + * group分析 | ||
854 | + * @access protected | ||
855 | + * @param Query $query 查询对象 | ||
856 | + * @param mixed $group | ||
857 | + * @return string | ||
858 | + */ | ||
859 | + protected function parseGroup(Query $query, $group) | ||
860 | + { | ||
861 | + return !empty($group) ? ' GROUP BY ' . $this->parseKey($query, $group) : ''; | ||
862 | + } | ||
863 | + | ||
864 | + /** | ||
865 | + * having分析 | ||
866 | + * @access protected | ||
867 | + * @param Query $query 查询对象 | ||
868 | + * @param string $having | ||
869 | + * @return string | ||
870 | + */ | ||
871 | + protected function parseHaving(Query $query, $having) | ||
872 | + { | ||
873 | + return !empty($having) ? ' HAVING ' . $having : ''; | ||
874 | + } | ||
875 | + | ||
876 | + /** | ||
877 | + * comment分析 | ||
878 | + * @access protected | ||
879 | + * @param Query $query 查询对象 | ||
880 | + * @param string $comment | ||
881 | + * @return string | ||
882 | + */ | ||
883 | + protected function parseComment(Query $query, $comment) | ||
884 | + { | ||
885 | + if (false !== strpos($comment, '*/')) { | ||
886 | + $comment = strstr($comment, '*/', true); | ||
887 | + } | ||
888 | + | ||
889 | + return !empty($comment) ? ' /* ' . $comment . ' */' : ''; | ||
890 | + } | ||
891 | + | ||
892 | + /** | ||
893 | + * distinct分析 | ||
894 | + * @access protected | ||
895 | + * @param Query $query 查询对象 | ||
896 | + * @param mixed $distinct | ||
897 | + * @return string | ||
898 | + */ | ||
899 | + protected function parseDistinct(Query $query, $distinct) | ||
900 | + { | ||
901 | + return !empty($distinct) ? ' DISTINCT ' : ''; | ||
902 | + } | ||
903 | + | ||
904 | + /** | ||
905 | + * union分析 | ||
906 | + * @access protected | ||
907 | + * @param Query $query 查询对象 | ||
908 | + * @param mixed $union | ||
909 | + * @return string | ||
910 | + */ | ||
911 | + protected function parseUnion(Query $query, $union) | ||
912 | + { | ||
913 | + if (empty($union)) { | ||
914 | + return ''; | ||
915 | + } | ||
916 | + | ||
917 | + $type = $union['type']; | ||
918 | + unset($union['type']); | ||
919 | + | ||
920 | + foreach ($union as $u) { | ||
921 | + if ($u instanceof \Closure) { | ||
922 | + $sql[] = $type . ' ' . $this->parseClosure($query, $u); | ||
923 | + } elseif (is_string($u)) { | ||
924 | + $sql[] = $type . ' ( ' . $this->connection->parseSqlTable($u) . ' )'; | ||
925 | + } | ||
926 | + } | ||
927 | + | ||
928 | + return ' ' . implode(' ', $sql); | ||
929 | + } | ||
930 | + | ||
931 | + /** | ||
932 | + * index分析,可在操作链中指定需要强制使用的索引 | ||
933 | + * @access protected | ||
934 | + * @param Query $query 查询对象 | ||
935 | + * @param mixed $index | ||
936 | + * @return string | ||
937 | + */ | ||
938 | + protected function parseForce(Query $query, $index) | ||
939 | + { | ||
940 | + if (empty($index)) { | ||
941 | + return ''; | ||
942 | + } | ||
943 | + | ||
944 | + if (is_array($index)) { | ||
945 | + $index = join(",", $index); | ||
946 | + } | ||
947 | + | ||
948 | + return sprintf(" FORCE INDEX ( %s ) ", $index); | ||
949 | + } | ||
950 | + | ||
951 | + /** | ||
952 | + * 设置锁机制 | ||
953 | + * @access protected | ||
954 | + * @param Query $query 查询对象 | ||
955 | + * @param bool|string $lock | ||
956 | + * @return string | ||
957 | + */ | ||
958 | + protected function parseLock(Query $query, $lock = false) | ||
959 | + { | ||
960 | + if (is_bool($lock)) { | ||
961 | + return $lock ? ' FOR UPDATE ' : ''; | ||
962 | + } elseif (is_string($lock) && !empty($lock)) { | ||
963 | + return ' ' . trim($lock) . ' '; | ||
964 | + } | ||
965 | + } | ||
966 | + | ||
967 | + /** | ||
968 | + * 生成查询SQL | ||
969 | + * @access public | ||
970 | + * @param Query $query 查询对象 | ||
971 | + * @return string | ||
972 | + */ | ||
973 | + public function select(Query $query) | ||
974 | + { | ||
975 | + $options = $query->getOptions(); | ||
976 | + | ||
977 | + return str_replace( | ||
978 | + ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], | ||
979 | + [ | ||
980 | + $this->parseTable($query, $options['table']), | ||
981 | + $this->parseDistinct($query, $options['distinct']), | ||
982 | + $this->parseField($query, $options['field']), | ||
983 | + $this->parseJoin($query, $options['join']), | ||
984 | + $this->parseWhere($query, $options['where']), | ||
985 | + $this->parseGroup($query, $options['group']), | ||
986 | + $this->parseHaving($query, $options['having']), | ||
987 | + $this->parseOrder($query, $options['order']), | ||
988 | + $this->parseLimit($query, $options['limit']), | ||
989 | + $this->parseUnion($query, $options['union']), | ||
990 | + $this->parseLock($query, $options['lock']), | ||
991 | + $this->parseComment($query, $options['comment']), | ||
992 | + $this->parseForce($query, $options['force']), | ||
993 | + ], | ||
994 | + $this->selectSql); | ||
995 | + } | ||
996 | + | ||
997 | + /** | ||
998 | + * 生成Insert SQL | ||
999 | + * @access public | ||
1000 | + * @param Query $query 查询对象 | ||
1001 | + * @param bool $replace 是否replace | ||
1002 | + * @return string | ||
1003 | + */ | ||
1004 | + public function insert(Query $query, $replace = false) | ||
1005 | + { | ||
1006 | + $options = $query->getOptions(); | ||
1007 | + | ||
1008 | + // 分析并处理数据 | ||
1009 | + $data = $this->parseData($query, $options['data']); | ||
1010 | + if (empty($data)) { | ||
1011 | + return ''; | ||
1012 | + } | ||
1013 | + | ||
1014 | + $fields = array_keys($data); | ||
1015 | + $values = array_values($data); | ||
1016 | + | ||
1017 | + return str_replace( | ||
1018 | + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], | ||
1019 | + [ | ||
1020 | + $replace ? 'REPLACE' : 'INSERT', | ||
1021 | + $this->parseTable($query, $options['table']), | ||
1022 | + implode(' , ', $fields), | ||
1023 | + implode(' , ', $values), | ||
1024 | + $this->parseComment($query, $options['comment']), | ||
1025 | + ], | ||
1026 | + $this->insertSql); | ||
1027 | + } | ||
1028 | + | ||
1029 | + /** | ||
1030 | + * 生成insertall SQL | ||
1031 | + * @access public | ||
1032 | + * @param Query $query 查询对象 | ||
1033 | + * @param array $dataSet 数据集 | ||
1034 | + * @param bool $replace 是否replace | ||
1035 | + * @return string | ||
1036 | + */ | ||
1037 | + public function insertAll(Query $query, $dataSet, $replace = false) | ||
1038 | + { | ||
1039 | + $options = $query->getOptions(); | ||
1040 | + | ||
1041 | + // 获取合法的字段 | ||
1042 | + if ('*' == $options['field']) { | ||
1043 | + $allowFields = $this->connection->getTableFields($options['table']); | ||
1044 | + } else { | ||
1045 | + $allowFields = $options['field']; | ||
1046 | + } | ||
1047 | + // 获取绑定信息 | ||
1048 | + $bind = $this->connection->getFieldsBind($options['table']); | ||
1049 | + | ||
1050 | + foreach ($dataSet as $data) { | ||
1051 | + $data = $this->parseData($query, $data, $allowFields, $bind); | ||
1052 | + | ||
1053 | + $values[] = 'SELECT ' . implode(',', array_values($data)); | ||
1054 | + | ||
1055 | + if (!isset($insertFields)) { | ||
1056 | + $insertFields = array_keys($data); | ||
1057 | + } | ||
1058 | + } | ||
1059 | + | ||
1060 | + $fields = []; | ||
1061 | + | ||
1062 | + foreach ($insertFields as $field) { | ||
1063 | + $fields[] = $this->parseKey($query, $field); | ||
1064 | + } | ||
1065 | + | ||
1066 | + return str_replace( | ||
1067 | + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], | ||
1068 | + [ | ||
1069 | + $replace ? 'REPLACE' : 'INSERT', | ||
1070 | + $this->parseTable($query, $options['table']), | ||
1071 | + implode(' , ', $fields), | ||
1072 | + implode(' UNION ALL ', $values), | ||
1073 | + $this->parseComment($query, $options['comment']), | ||
1074 | + ], | ||
1075 | + $this->insertAllSql); | ||
1076 | + } | ||
1077 | + | ||
1078 | + /** | ||
1079 | + * 生成slect insert SQL | ||
1080 | + * @access public | ||
1081 | + * @param Query $query 查询对象 | ||
1082 | + * @param array $fields 数据 | ||
1083 | + * @param string $table 数据表 | ||
1084 | + * @return string | ||
1085 | + */ | ||
1086 | + public function selectInsert(Query $query, $fields, $table) | ||
1087 | + { | ||
1088 | + if (is_string($fields)) { | ||
1089 | + $fields = explode(',', $fields); | ||
1090 | + } | ||
1091 | + | ||
1092 | + foreach ($fields as &$field) { | ||
1093 | + $field = $this->parseKey($query, $field, true); | ||
1094 | + } | ||
1095 | + | ||
1096 | + return 'INSERT INTO ' . $this->parseTable($query, $table) . ' (' . implode(',', $fields) . ') ' . $this->select($query); | ||
1097 | + } | ||
1098 | + | ||
1099 | + /** | ||
1100 | + * 生成update SQL | ||
1101 | + * @access public | ||
1102 | + * @param Query $query 查询对象 | ||
1103 | + * @return string | ||
1104 | + */ | ||
1105 | + public function update(Query $query) | ||
1106 | + { | ||
1107 | + $options = $query->getOptions(); | ||
1108 | + | ||
1109 | + $table = $this->parseTable($query, $options['table']); | ||
1110 | + $data = $this->parseData($query, $options['data']); | ||
1111 | + | ||
1112 | + if (empty($data)) { | ||
1113 | + return ''; | ||
1114 | + } | ||
1115 | + | ||
1116 | + foreach ($data as $key => $val) { | ||
1117 | + $set[] = $key . ' = ' . $val; | ||
1118 | + } | ||
1119 | + | ||
1120 | + return str_replace( | ||
1121 | + ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], | ||
1122 | + [ | ||
1123 | + $this->parseTable($query, $options['table']), | ||
1124 | + implode(' , ', $set), | ||
1125 | + $this->parseJoin($query, $options['join']), | ||
1126 | + $this->parseWhere($query, $options['where']), | ||
1127 | + $this->parseOrder($query, $options['order']), | ||
1128 | + $this->parseLimit($query, $options['limit']), | ||
1129 | + $this->parseLock($query, $options['lock']), | ||
1130 | + $this->parseComment($query, $options['comment']), | ||
1131 | + ], | ||
1132 | + $this->updateSql); | ||
1133 | + } | ||
1134 | + | ||
1135 | + /** | ||
1136 | + * 生成delete SQL | ||
1137 | + * @access public | ||
1138 | + * @param Query $query 查询对象 | ||
1139 | + * @return string | ||
1140 | + */ | ||
1141 | + public function delete(Query $query) | ||
1142 | + { | ||
1143 | + $options = $query->getOptions(); | ||
1144 | + | ||
1145 | + return str_replace( | ||
1146 | + ['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], | ||
1147 | + [ | ||
1148 | + $this->parseTable($query, $options['table']), | ||
1149 | + !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '', | ||
1150 | + $this->parseJoin($query, $options['join']), | ||
1151 | + $this->parseWhere($query, $options['where']), | ||
1152 | + $this->parseOrder($query, $options['order']), | ||
1153 | + $this->parseLimit($query, $options['limit']), | ||
1154 | + $this->parseLock($query, $options['lock']), | ||
1155 | + $this->parseComment($query, $options['comment']), | ||
1156 | + ], | ||
1157 | + $this->deleteSql); | ||
1158 | + } | ||
1159 | +} |
-
请 注册 或 登录 后发表评论