PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.3.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.3.1
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Repositories / ClientRepository.php

ClientRepository.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.3.1, at includes/Repositories/ClientRepository.php

441 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Client Repository Class
4 *
5 * @package Easy_Invoice
6 * @subpackage Repositories
7 */
8
9 namespace EasyInvoice\Repositories;
10
11 use EasyInvoice\Constants\ClientFields;
12 use EasyInvoice\Interfaces\ClientRepositoryInterface;
13 use EasyInvoice\Models\Client;
14 use WP_User;
15 use WP_User_Query;
16 use WP_Query;
17
18 /**
19 * ClientRepository Class
20 *
21 * Handles data access for client objects using WordPress users.
22 */
23 class ClientRepository implements ClientRepositoryInterface {
24
25 /**
26 * Find a client by ID
27 *
28 * @param int $id The client ID
29 * @return Client|null The client model or null if not found
30 */
31 public function find($id) {
32 // Regular client (WordPress user)
33 $user = get_user_by('id', $id);
34
35 if (!$user) {
36 return null;
37 }
38
39 return $this->createClientFromUser($user);
40 }
41
42 /**
43 * Get all clients
44 *
45 * @param array $args Optional arguments to filter the results
46 * @return array Array of Client models
47 */
48 public function all($args = []) {
49 $clients = [];
50
51 // Get all users to include administrators and other users
52 $user_query = new WP_User_Query([
53 'number' => -1,
54 'orderby' => 'ID',
55 'order' => 'DESC',
56 'role__not_in' => ['Administrator'], // exclude admins
57 ]);
58
59 foreach ($user_query->get_results() as $user) {
60 $client = $this->createClientFromUser($user);
61 if ($client) {
62 $clients[] = $client;
63 }
64 }
65
66 return $clients;
67 }
68
69 /**
70 * Create a new client
71 *
72 * @param array $data The client data
73 * @return Client The created client model
74 */
75 public function create($data) {
76 // Create regular client as WordPress user
77 return $this->createRegularClient($data);
78 }
79
80 /**
81 * Create a regular client (WordPress user)
82 *
83 * @param array $data The client data
84 * @return Client|null The created client model
85 */
86 protected function createRegularClient($data) {
87 // Create a new user if email is provided
88 if (empty($data[ClientFields::EMAIL])) {
89 return null;
90 }
91
92 $user_data = [
93 'user_email' => $data[ClientFields::EMAIL],
94 'user_login' => $data[ClientFields::USERNAME],
95 'user_pass' => !empty($data[ClientFields::PASSWORD]) ? $data[ClientFields::PASSWORD] : wp_generate_password(),
96 'display_name' => $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME],
97 'first_name' => $data[ClientFields::FIRST_NAME],
98 'last_name' => $data[ClientFields::LAST_NAME],
99 'role' => 'customer',
100 ];
101
102 $user_id = wp_insert_user($user_data);
103
104 if (is_wp_error($user_id)) {
105 return null;
106 }
107
108 // Create client model
109 $client = new Client($user_id);
110
111 // Set the client data
112 $this->setClientData($client, $data);
113
114 return $client;
115 }
116
117 /**
118 * Update an existing client
119 *
120 * @param int $id The client ID
121 * @param array $data The client data
122 * @return Client|null The updated client model or null if not found
123 */
124 public function update($id, $data) {
125 $client = $this->find($id);
126
127 if (!$client) {
128 return null;
129 }
130
131 // Regular client - update user data if email is provided
132 $user_data = ['ID' => $id];
133 if (!empty($data[ClientFields::EMAIL])) {
134 $user_data['user_email'] = $data[ClientFields::EMAIL];
135 }
136 if (!empty($data[ClientFields::USERNAME])) {
137 $user_data['user_login'] = $data[ClientFields::USERNAME];
138 }
139 if (!empty($data[ClientFields::PASSWORD])) {
140 $user_data['user_pass'] = $data[ClientFields::PASSWORD];
141 }
142 if (!empty($data[ClientFields::FIRST_NAME]) && !empty($data[ClientFields::LAST_NAME])) {
143 $user_data['display_name'] = $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME];
144 }
145 if (!empty($data[ClientFields::FIRST_NAME])) {
146 $user_data['first_name'] = $data[ClientFields::FIRST_NAME];
147 }
148 if (!empty($data[ClientFields::LAST_NAME])) {
149 $user_data['last_name'] = $data[ClientFields::LAST_NAME];
150 }
151
152 // Only update user data if we have more than just the ID
153 if(count($user_data) > 1) {
154 $result = wp_update_user($user_data);
155 if (is_wp_error($result)) {
156 // Log the error but continue with client data update
157 }
158 }
159
160 // Set the client data
161 $this->setClientData($client, $data);
162
163 return $client;
164 }
165
166 /**
167 * Delete a client
168 *
169 * @param int $id The client ID
170 * @return bool True if successful, false otherwise
171 */
172 public function delete($id) {
173 // Check if client exists
174 $client = $this->find($id);
175
176 if (!$client) {
177 return false;
178 }
179
180 // Check if user exists and is not an administrator
181 $user = get_user_by('ID', $id);
182 if (!$user || in_array('administrator', $user->roles)) {
183 return false;
184 }
185
186 // Delete all invoices and quotes associated with this client
187 global $wpdb;
188
189 // Get all invoices and quotes for this client
190 $posts = $wpdb->get_results($wpdb->prepare(
191 "SELECT ID, post_type FROM {$wpdb->posts} WHERE post_type IN ('easy_invoice', 'easy_invoice_quote') AND ID IN (
192 SELECT post_id FROM {$wpdb->postmeta}
193 WHERE (meta_key = '_easy_invoice_client_id' OR meta_key = '_easy_invoice_quote_client_id')
194 AND meta_value = %d
195 )",
196 $id
197 ));
198
199 // Delete each post and its meta
200 foreach ($posts as $post) {
201 wp_delete_post($post->ID, true);
202 }
203
204 // Delete all payments associated with this client
205 $payments = $wpdb->get_col($wpdb->prepare(
206 "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'easy_payment' AND ID IN (
207 SELECT post_id FROM {$wpdb->postmeta}
208 WHERE meta_key = '_easy_payment_client_id' AND meta_value = %d
209 )",
210 $id
211 ));
212
213 foreach ($payments as $payment_id) {
214 wp_delete_post($payment_id, true);
215 }
216
217 // Delete the WordPress user (this will also delete all user meta)
218 $result = wp_delete_user($id);
219
220 // Return the result
221 return $result;
222
223 return $result;
224 }
225
226 /**
227 * Find clients by email
228 *
229 * @param string $email The client email
230 * @return array Array of Client models
231 */
232 public function findByEmail($email) {
233 $clients = [];
234
235 // Find regular clients by email
236 $user = get_user_by('email', $email);
237
238 if ($user) {
239 $clients[] = $this->createClientFromUser($user);
240 }
241
242 return $clients;
243 }
244
245 /**
246 * Find clients by business/client name
247 *
248 * @param string $business_client_name The business/client name
249 * @return array Array of Client models
250 */
251 public function findByBusinessClientName($business_client_name) {
252 $args = [
253 'meta_query' => [
254 [
255 'key' => ClientFields::BUSINESS_CLIENT_NAME,
256 'value' => $business_client_name,
257 'compare' => 'LIKE',
258 ],
259 ],
260 ];
261
262 return $this->all($args);
263 }
264
265 /**
266 * Search clients by name, email, company, or phone.
267 *
268 * Matches against:
269 * - User table columns: display_name, user_login, user_email,
270 * user_nicename, user_url (these are what WP_User_Query's
271 * `search` + `search_columns` can actually index).
272 * - WP standard meta: first_name, last_name, nickname.
273 * - WooCommerce billing meta: billing_first_name, billing_last_name,
274 * billing_email, billing_company, billing_phone — so customers
275 * imported via WooCommerce can be found by their billing details.
276 * - Easy Invoice's own meta: first/last/email/business name.
277 *
278 * The previous implementation gated every query on a meta_query
279 * requiring `_easy_invoice_client_business_client_name` OR
280 * `_easy_invoice_client_email` to EXIST, which silently excluded
281 * every WooCommerce customer (they don't have those EI-specific
282 * keys until they've been edited inside Easy Invoice). On stores
283 * with hundreds of imported WC customers, search returned nothing
284 * even though the unfiltered list showed every client.
285 *
286 * @param string $query The search query
287 * @return array Array of Client models
288 */
289 public function search($query) {
290 $query = trim((string) $query);
291
292 if ($query === '') {
293 return $this->all();
294 }
295
296 $clients = [];
297 $seen_ids = [];
298 $like = '*' . $query . '*';
299
300 // ── 1) User table columns ──────────────────────────────────────
301 // These are the only columns WP_User_Query's `search_columns`
302 // accepts; passing `first_name` etc. is silently ignored, so we
303 // pick those up via meta in step 2 below.
304 $user_query = new WP_User_Query([
305 'number' => 50,
306 'orderby' => 'display_name',
307 'order' => 'ASC',
308 'search' => $like,
309 'search_columns' => ['user_login', 'user_email', 'user_nicename', 'display_name', 'user_url'],
310 'role__not_in' => ['Administrator'],
311 ]);
312 foreach ($user_query->get_results() as $user) {
313 if (isset($seen_ids[$user->ID])) {
314 continue;
315 }
316 $seen_ids[$user->ID] = true;
317 $client = $this->createClientFromUser($user);
318 if ($client) {
319 $clients[] = $client;
320 }
321 }
322
323 // ── 2) User-meta columns ───────────────────────────────────────
324 // Search across every meta key we know a client's name / email /
325 // company / phone could be stored under. WP-standard, WooCommerce
326 // billing_*, and Easy Invoice's own keys are all OR'd together so
327 // the user only has to match ONE for a row to qualify.
328 $meta_keys = [
329 // WP standard
330 'first_name', 'last_name', 'nickname',
331 // WooCommerce billing — covers imported store customers
332 'billing_first_name', 'billing_last_name', 'billing_email',
333 'billing_company', 'billing_phone',
334 // Easy Invoice's own
335 ClientFields::FIRST_NAME, ClientFields::LAST_NAME,
336 ClientFields::EMAIL, ClientFields::BUSINESS_CLIENT_NAME,
337 ];
338
339 $meta_query = ['relation' => 'OR'];
340 foreach ($meta_keys as $key) {
341 $meta_query[] = [
342 'key' => $key,
343 'value' => $query,
344 'compare' => 'LIKE',
345 ];
346 }
347
348 $meta_user_query = new WP_User_Query([
349 'number' => 50,
350 'orderby' => 'display_name',
351 'order' => 'ASC',
352 'role__not_in' => ['Administrator'],
353 'meta_query' => $meta_query,
354 ]);
355 foreach ($meta_user_query->get_results() as $user) {
356 if (isset($seen_ids[$user->ID])) {
357 continue;
358 }
359 $seen_ids[$user->ID] = true;
360 $client = $this->createClientFromUser($user);
361 if ($client) {
362 $clients[] = $client;
363 }
364 }
365
366 // Cap the dropdown at 50 results so the autocomplete stays
367 // responsive on stores with thousands of customers. The user
368 // can refine the query to narrow further.
369 return array_slice($clients, 0, 50);
370 }
371
372 /**
373 * Create a client model from a WordPress user
374 *
375 * @param WP_User $user The WordPress user
376 * @return Client The client model
377 */
378 protected function createClientFromUser(WP_User $user) {
379 try {
380 $client = new Client($user->ID);
381 return $client;
382 } catch (\Exception $e) {
383 return null;
384 }
385 }
386
387 /**
388 * Set the client data
389 *
390 * @param Client $client The client model
391 * @param array $data The client data
392 */
393 protected function setClientData(Client $client, array $data) {
394 if (isset($data[ClientFields::BUSINESS_CLIENT_NAME])) {
395 $client->business_client_name = $data[ClientFields::BUSINESS_CLIENT_NAME];
396 update_user_meta($client->getId(), ClientFields::BUSINESS_CLIENT_NAME, $data[ClientFields::BUSINESS_CLIENT_NAME]);
397 }
398
399 if (isset($data[ClientFields::EMAIL])) {
400 $client->email = $data[ClientFields::EMAIL];
401 update_user_meta($client->getId(), ClientFields::EMAIL, $data[ClientFields::EMAIL]);
402 }
403
404 if (isset($data[ClientFields::USERNAME])) {
405 $client->username = $data[ClientFields::USERNAME];
406 update_user_meta($client->getId(), ClientFields::USERNAME, $data[ClientFields::USERNAME]);
407 }
408
409 if (isset($data[ClientFields::ADDRESS])) {
410 $client->address = $data[ClientFields::ADDRESS];
411 update_user_meta($client->getId(), ClientFields::ADDRESS, $data[ClientFields::ADDRESS]);
412 }
413
414 if (isset($data[ClientFields::EXTRA_INFO])) {
415 $client->extra_info = $data[ClientFields::EXTRA_INFO];
416 update_user_meta($client->getId(), ClientFields::EXTRA_INFO, $data[ClientFields::EXTRA_INFO]);
417 }
418
419 if (isset($data[ClientFields::FIRST_NAME])) {
420 $client->first_name = $data[ClientFields::FIRST_NAME];
421 update_user_meta($client->getId(), ClientFields::FIRST_NAME, $data[ClientFields::FIRST_NAME]);
422 }
423
424 if (isset($data[ClientFields::LAST_NAME])) {
425 $client->last_name = $data[ClientFields::LAST_NAME];
426 update_user_meta($client->getId(), ClientFields::LAST_NAME, $data[ClientFields::LAST_NAME]);
427 }
428
429 if (isset($data[ClientFields::WEBSITE])) {
430 $client->website = $data[ClientFields::WEBSITE];
431 update_user_meta($client->getId(), ClientFields::WEBSITE, $data[ClientFields::WEBSITE]);
432 }
433 if (isset($data[ClientFields::PHONE])) {
434 $client->phone = $data[ClientFields::PHONE];
435 update_user_meta($client->getId(), ClientFields::PHONE, $data[ClientFields::PHONE]);
436 }
437
438 // Reset the dirty flag after saving
439 $client->resetDirty();
440 }
441 }