PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.3.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.3.2
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.2, at includes/Repositories/ClientRepository.php

444 lines 15.1 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 — note PHONE here too so EI-native clients
335 // (whose phone lives in _easy_invoice_client_phone, not billing_*)
336 // are searchable by phone number on equal footing with WC customers.
337 ClientFields::FIRST_NAME, ClientFields::LAST_NAME,
338 ClientFields::EMAIL, ClientFields::BUSINESS_CLIENT_NAME,
339 ClientFields::PHONE,
340 ];
341
342 $meta_query = ['relation' => 'OR'];
343 foreach ($meta_keys as $key) {
344 $meta_query[] = [
345 'key' => $key,
346 'value' => $query,
347 'compare' => 'LIKE',
348 ];
349 }
350
351 $meta_user_query = new WP_User_Query([
352 'number' => 50,
353 'orderby' => 'display_name',
354 'order' => 'ASC',
355 'role__not_in' => ['Administrator'],
356 'meta_query' => $meta_query,
357 ]);
358 foreach ($meta_user_query->get_results() as $user) {
359 if (isset($seen_ids[$user->ID])) {
360 continue;
361 }
362 $seen_ids[$user->ID] = true;
363 $client = $this->createClientFromUser($user);
364 if ($client) {
365 $clients[] = $client;
366 }
367 }
368
369 // Cap the dropdown at 50 results so the autocomplete stays
370 // responsive on stores with thousands of customers. The user
371 // can refine the query to narrow further.
372 return array_slice($clients, 0, 50);
373 }
374
375 /**
376 * Create a client model from a WordPress user
377 *
378 * @param WP_User $user The WordPress user
379 * @return Client The client model
380 */
381 protected function createClientFromUser(WP_User $user) {
382 try {
383 $client = new Client($user->ID);
384 return $client;
385 } catch (\Exception $e) {
386 return null;
387 }
388 }
389
390 /**
391 * Set the client data
392 *
393 * @param Client $client The client model
394 * @param array $data The client data
395 */
396 protected function setClientData(Client $client, array $data) {
397 if (isset($data[ClientFields::BUSINESS_CLIENT_NAME])) {
398 $client->business_client_name = $data[ClientFields::BUSINESS_CLIENT_NAME];
399 update_user_meta($client->getId(), ClientFields::BUSINESS_CLIENT_NAME, $data[ClientFields::BUSINESS_CLIENT_NAME]);
400 }
401
402 if (isset($data[ClientFields::EMAIL])) {
403 $client->email = $data[ClientFields::EMAIL];
404 update_user_meta($client->getId(), ClientFields::EMAIL, $data[ClientFields::EMAIL]);
405 }
406
407 if (isset($data[ClientFields::USERNAME])) {
408 $client->username = $data[ClientFields::USERNAME];
409 update_user_meta($client->getId(), ClientFields::USERNAME, $data[ClientFields::USERNAME]);
410 }
411
412 if (isset($data[ClientFields::ADDRESS])) {
413 $client->address = $data[ClientFields::ADDRESS];
414 update_user_meta($client->getId(), ClientFields::ADDRESS, $data[ClientFields::ADDRESS]);
415 }
416
417 if (isset($data[ClientFields::EXTRA_INFO])) {
418 $client->extra_info = $data[ClientFields::EXTRA_INFO];
419 update_user_meta($client->getId(), ClientFields::EXTRA_INFO, $data[ClientFields::EXTRA_INFO]);
420 }
421
422 if (isset($data[ClientFields::FIRST_NAME])) {
423 $client->first_name = $data[ClientFields::FIRST_NAME];
424 update_user_meta($client->getId(), ClientFields::FIRST_NAME, $data[ClientFields::FIRST_NAME]);
425 }
426
427 if (isset($data[ClientFields::LAST_NAME])) {
428 $client->last_name = $data[ClientFields::LAST_NAME];
429 update_user_meta($client->getId(), ClientFields::LAST_NAME, $data[ClientFields::LAST_NAME]);
430 }
431
432 if (isset($data[ClientFields::WEBSITE])) {
433 $client->website = $data[ClientFields::WEBSITE];
434 update_user_meta($client->getId(), ClientFields::WEBSITE, $data[ClientFields::WEBSITE]);
435 }
436 if (isset($data[ClientFields::PHONE])) {
437 $client->phone = $data[ClientFields::PHONE];
438 update_user_meta($client->getId(), ClientFields::PHONE, $data[ClientFields::PHONE]);
439 }
440
441 // Reset the dirty flag after saving
442 $client->resetDirty();
443 }
444 }