# easy-invoice/2.3.2/includes/Repositories/ClientRepository.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.2. 444 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.2/code/includes/Repositories/ClientRepository.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.2/raw/includes/Repositories/ClientRepository.php
- Modified: 2026-05-24T14:33:32+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.2/code/includes/Repositories/ClientRepository.php#L10-L20`.

```php
<?php
/**
 * Client Repository Class
 *
 * @package Easy_Invoice
 * @subpackage Repositories
 */

namespace EasyInvoice\Repositories;

use EasyInvoice\Constants\ClientFields;
use EasyInvoice\Interfaces\ClientRepositoryInterface;
use EasyInvoice\Models\Client;
use WP_User;
use WP_User_Query;
use WP_Query;

/**
 * ClientRepository Class
 * 
 * Handles data access for client objects using WordPress users.
 */
class ClientRepository implements ClientRepositoryInterface {
    
    /**
     * Find a client by ID
     *
     * @param int $id The client ID
     * @return Client|null The client model or null if not found
     */
    public function find($id) {
        // Regular client (WordPress user)
        $user = get_user_by('id', $id);
        
        if (!$user) {
            return null;
        }
        
        return $this->createClientFromUser($user);
    }
    
    /**
     * Get all clients
     *
     * @param array $args Optional arguments to filter the results
     * @return array Array of Client models
     */
    public function all($args = []) {
        $clients = [];
        
        // Get all users to include administrators and other users
        $user_query = new WP_User_Query([
            'number' => -1,
            'orderby' => 'ID',
            'order' => 'DESC',
            'role__not_in' => ['Administrator'], // exclude admins
        ]);
        
        foreach ($user_query->get_results() as $user) {
            $client = $this->createClientFromUser($user);
            if ($client) {
                $clients[] = $client;
            }
        }
        
        return $clients;
    }
    
    /**
     * Create a new client
     *
     * @param array $data The client data
     * @return Client The created client model
     */
    public function create($data) {
        // Create regular client as WordPress user
        return $this->createRegularClient($data);
    }
    
    /**
     * Create a regular client (WordPress user)
     *
     * @param array $data The client data
     * @return Client|null The created client model
     */
    protected function createRegularClient($data) {
        // Create a new user if email is provided
        if (empty($data[ClientFields::EMAIL])) {
            return null;
        }
       
        $user_data = [
            'user_email' => $data[ClientFields::EMAIL],
            'user_login' => $data[ClientFields::USERNAME],
            'user_pass' => !empty($data[ClientFields::PASSWORD]) ? $data[ClientFields::PASSWORD] : wp_generate_password(),
            'display_name' => $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME],
            'first_name' => $data[ClientFields::FIRST_NAME],
            'last_name' => $data[ClientFields::LAST_NAME],
            'role' => 'customer',
        ];
        
        $user_id = wp_insert_user($user_data);
        
        if (is_wp_error($user_id)) {
            return null;
        }
        
        // Create client model
        $client = new Client($user_id);
        
        // Set the client data
        $this->setClientData($client, $data);
        
        return $client;
    }
    
    /**
     * Update an existing client
     *
     * @param int $id The client ID
     * @param array $data The client data
     * @return Client|null The updated client model or null if not found
     */
    public function update($id, $data) {
        $client = $this->find($id);
        
        if (!$client) {
            return null;
        }
        
        // Regular client - update user data if email is provided
        $user_data = ['ID' => $id];
        if (!empty($data[ClientFields::EMAIL])) {
            $user_data['user_email'] = $data[ClientFields::EMAIL];
        }
        if (!empty($data[ClientFields::USERNAME])) {
            $user_data['user_login'] = $data[ClientFields::USERNAME];
        }
        if (!empty($data[ClientFields::PASSWORD])) {
            $user_data['user_pass'] = $data[ClientFields::PASSWORD];
        }
        if (!empty($data[ClientFields::FIRST_NAME]) && !empty($data[ClientFields::LAST_NAME])) {
            $user_data['display_name'] = $data[ClientFields::FIRST_NAME] . ' ' . $data[ClientFields::LAST_NAME];
        }
        if (!empty($data[ClientFields::FIRST_NAME])) {
            $user_data['first_name'] = $data[ClientFields::FIRST_NAME];
        }
        if (!empty($data[ClientFields::LAST_NAME])) {
            $user_data['last_name'] = $data[ClientFields::LAST_NAME];
        }

        // Only update user data if we have more than just the ID
        if(count($user_data) > 1) {
            $result = wp_update_user($user_data);
            if (is_wp_error($result)) {
                // Log the error but continue with client data update
            }
        }
        
        // Set the client data
        $this->setClientData($client, $data);
        
        return $client;
    }
    
    /**
     * Delete a client
     *
     * @param int $id The client ID
     * @return bool True if successful, false otherwise
     */
    public function delete($id) {
        // Check if client exists
        $client = $this->find($id);
        
        if (!$client) {
            return false;
        }
        
        // Check if user exists and is not an administrator
        $user = get_user_by('ID', $id);
        if (!$user || in_array('administrator', $user->roles)) {
            return false;
        }
        
        // Delete all invoices and quotes associated with this client
        global $wpdb;
        
        // Get all invoices and quotes for this client
        $posts = $wpdb->get_results($wpdb->prepare(
            "SELECT ID, post_type FROM {$wpdb->posts} WHERE post_type IN ('easy_invoice', 'easy_invoice_quote') AND ID IN (
                SELECT post_id FROM {$wpdb->postmeta} 
                WHERE (meta_key = '_easy_invoice_client_id' OR meta_key = '_easy_invoice_quote_client_id') 
                AND meta_value = %d
            )",
            $id
        ));
        
        // Delete each post and its meta
        foreach ($posts as $post) {
            wp_delete_post($post->ID, true);
        }
        
        // Delete all payments associated with this client
        $payments = $wpdb->get_col($wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'easy_payment' AND ID IN (
                SELECT post_id FROM {$wpdb->postmeta} 
                WHERE meta_key = '_easy_payment_client_id' AND meta_value = %d
            )",
            $id
        ));
        
        foreach ($payments as $payment_id) {
            wp_delete_post($payment_id, true);
        }
        
        // Delete the WordPress user (this will also delete all user meta)
        $result = wp_delete_user($id);
        
        // Return the result
        return $result;
        
        return $result;
    }
    
    /**
     * Find clients by email
     *
     * @param string $email The client email
     * @return array Array of Client models
     */
    public function findByEmail($email) {
        $clients = [];
        
        // Find regular clients by email
        $user = get_user_by('email', $email);
        
        if ($user) {
            $clients[] = $this->createClientFromUser($user);
        }
        
        return $clients;
    }
    
    /**
     * Find clients by business/client name
     *
     * @param string $business_client_name The business/client name
     * @return array Array of Client models
     */
    public function findByBusinessClientName($business_client_name) {
        $args = [
            'meta_query' => [
                [
                    'key' => ClientFields::BUSINESS_CLIENT_NAME,
                    'value' => $business_client_name,
                    'compare' => 'LIKE',
                ],
            ],
        ];
        
        return $this->all($args);
    }
    
    /**
     * Search clients by name, email, company, or phone.
     *
     * Matches against:
     *   - User table columns: display_name, user_login, user_email,
     *     user_nicename, user_url (these are what WP_User_Query's
     *     `search` + `search_columns` can actually index).
     *   - WP standard meta: first_name, last_name, nickname.
     *   - WooCommerce billing meta: billing_first_name, billing_last_name,
     *     billing_email, billing_company, billing_phone — so customers
     *     imported via WooCommerce can be found by their billing details.
     *   - Easy Invoice's own meta: first/last/email/business name.
     *
     * The previous implementation gated every query on a meta_query
     * requiring `_easy_invoice_client_business_client_name` OR
     * `_easy_invoice_client_email` to EXIST, which silently excluded
     * every WooCommerce customer (they don't have those EI-specific
     * keys until they've been edited inside Easy Invoice). On stores
     * with hundreds of imported WC customers, search returned nothing
     * even though the unfiltered list showed every client.
     *
     * @param string $query The search query
     * @return array Array of Client models
     */
    public function search($query) {
        $query = trim((string) $query);

        if ($query === '') {
            return $this->all();
        }

        $clients   = [];
        $seen_ids  = [];
        $like      = '*' . $query . '*';

        // ── 1) User table columns ──────────────────────────────────────
        // These are the only columns WP_User_Query's `search_columns`
        // accepts; passing `first_name` etc. is silently ignored, so we
        // pick those up via meta in step 2 below.
        $user_query = new WP_User_Query([
            'number'         => 50,
            'orderby'        => 'display_name',
            'order'          => 'ASC',
            'search'         => $like,
            'search_columns' => ['user_login', 'user_email', 'user_nicename', 'display_name', 'user_url'],
            'role__not_in'   => ['Administrator'],
        ]);
        foreach ($user_query->get_results() as $user) {
            if (isset($seen_ids[$user->ID])) {
                continue;
            }
            $seen_ids[$user->ID] = true;
            $client = $this->createClientFromUser($user);
            if ($client) {
                $clients[] = $client;
            }
        }

        // ── 2) User-meta columns ───────────────────────────────────────
        // Search across every meta key we know a client's name / email /
        // company / phone could be stored under. WP-standard, WooCommerce
        // billing_*, and Easy Invoice's own keys are all OR'd together so
        // the user only has to match ONE for a row to qualify.
        $meta_keys = [
            // WP standard
            'first_name', 'last_name', 'nickname',
            // WooCommerce billing — covers imported store customers
            'billing_first_name', 'billing_last_name', 'billing_email',
            'billing_company', 'billing_phone',
            // Easy Invoice's own — note PHONE here too so EI-native clients
            // (whose phone lives in _easy_invoice_client_phone, not billing_*)
            // are searchable by phone number on equal footing with WC customers.
            ClientFields::FIRST_NAME, ClientFields::LAST_NAME,
            ClientFields::EMAIL, ClientFields::BUSINESS_CLIENT_NAME,
            ClientFields::PHONE,
        ];

        $meta_query = ['relation' => 'OR'];
        foreach ($meta_keys as $key) {
            $meta_query[] = [
                'key'     => $key,
                'value'   => $query,
                'compare' => 'LIKE',
            ];
        }

        $meta_user_query = new WP_User_Query([
            'number'       => 50,
            'orderby'      => 'display_name',
            'order'        => 'ASC',
            'role__not_in' => ['Administrator'],
            'meta_query'   => $meta_query,
        ]);
        foreach ($meta_user_query->get_results() as $user) {
            if (isset($seen_ids[$user->ID])) {
                continue;
            }
            $seen_ids[$user->ID] = true;
            $client = $this->createClientFromUser($user);
            if ($client) {
                $clients[] = $client;
            }
        }

        // Cap the dropdown at 50 results so the autocomplete stays
        // responsive on stores with thousands of customers. The user
        // can refine the query to narrow further.
        return array_slice($clients, 0, 50);
    }
    
    /**
     * Create a client model from a WordPress user
     *
     * @param WP_User $user The WordPress user
     * @return Client The client model
     */
    protected function createClientFromUser(WP_User $user) {
        try {
            $client = new Client($user->ID);
            return $client;
        } catch (\Exception $e) {
            return null;
        }
    }
    
    /**
     * Set the client data
     *
     * @param Client $client The client model
     * @param array $data The client data
     */
    protected function setClientData(Client $client, array $data) {
        if (isset($data[ClientFields::BUSINESS_CLIENT_NAME])) {
            $client->business_client_name = $data[ClientFields::BUSINESS_CLIENT_NAME];
            update_user_meta($client->getId(), ClientFields::BUSINESS_CLIENT_NAME, $data[ClientFields::BUSINESS_CLIENT_NAME]);
        }
        
        if (isset($data[ClientFields::EMAIL])) {
            $client->email = $data[ClientFields::EMAIL];
            update_user_meta($client->getId(), ClientFields::EMAIL, $data[ClientFields::EMAIL]);
        }

        if (isset($data[ClientFields::USERNAME])) {
            $client->username = $data[ClientFields::USERNAME];
            update_user_meta($client->getId(), ClientFields::USERNAME, $data[ClientFields::USERNAME]);
        }

        if (isset($data[ClientFields::ADDRESS])) {
            $client->address = $data[ClientFields::ADDRESS];
            update_user_meta($client->getId(), ClientFields::ADDRESS, $data[ClientFields::ADDRESS]);
        }

        if (isset($data[ClientFields::EXTRA_INFO])) {
            $client->extra_info = $data[ClientFields::EXTRA_INFO];
            update_user_meta($client->getId(), ClientFields::EXTRA_INFO, $data[ClientFields::EXTRA_INFO]);
        }

        if (isset($data[ClientFields::FIRST_NAME])) {
            $client->first_name = $data[ClientFields::FIRST_NAME];
            update_user_meta($client->getId(), ClientFields::FIRST_NAME, $data[ClientFields::FIRST_NAME]);
        }

        if (isset($data[ClientFields::LAST_NAME])) {
            $client->last_name = $data[ClientFields::LAST_NAME];
            update_user_meta($client->getId(), ClientFields::LAST_NAME, $data[ClientFields::LAST_NAME]);
        }

        if (isset($data[ClientFields::WEBSITE])) {
            $client->website = $data[ClientFields::WEBSITE];
            update_user_meta($client->getId(), ClientFields::WEBSITE, $data[ClientFields::WEBSITE]);
        }
        if (isset($data[ClientFields::PHONE])) {
            $client->phone = $data[ClientFields::PHONE];
            update_user_meta($client->getId(), ClientFields::PHONE, $data[ClientFields::PHONE]);
        }
        
        // Reset the dirty flag after saving
        $client->resetDirty();
    }
} 
```
