searchable; } public static function boot() { parent::boot(); static::creating(function ($model) { $model->person_type = static::$type; $model->hash = md5(time() . wp_generate_uuid4()); }); static::addGlobalScope(function (Builder $builder) { $builder->where('person_type', 'customer'); }); } public static function mappables() { return [ 'title' => __('Customer Title', 'fluent-support'), 'address_line_1' => __('Address Line 1', 'fluent-support'), 'address_line_2' => __('Address Line 2', 'fluent-support'), 'city' => __('City', 'fluent-support'), 'state' => __('State', 'fluent-support'), 'zip' => __('Zip Code', 'fluent-support'), 'country' => __('Country', 'fluent-support'), ]; } /** * Find the customer for this data or create one. An existing record is only * updated when the data carries a user_id or remote_uid. * * @param array $customerData * @return false|Customer */ public static function maybeCreateCustomer($customerData) { $customer = self::getCustomerFromData($customerData); $email = Arr::get($customerData, 'email'); $user = $email ? get_user_by('email', $email) : false; if ($user) { if ($user->first_name) { $customerData['first_name'] = $user->first_name; } if ($user->last_name) { $customerData['last_name'] = $user->last_name; } if (empty($customerData['first_name']) && empty($customerData['last_name'])) { $customerData['first_name'] = $user->display_name; } $customerData['user_id'] = $user->ID; } if (!$customer) { if (!empty($customerData['last_ip_address'])) { $customerData['ip_address'] = $customerData['last_ip_address']; } $customerData = self::explodeFullName($customerData); $customer = self::create($customerData); if ($customer && $customer->exists) { /* * Action on customer create * * @since v1.0.0 * @param object $customer */ do_action('fluent_support/customer_created', $customer); } return $customer; } // Never link an existing row to a different WP account by email match. // Linking happens on create, on registration, or by an agent. $incomingUserId = (int) Arr::get($customerData, 'user_id'); if ($incomingUserId && $incomingUserId !== (int) $customer->user_id) { unset($customerData['user_id']); } // The incoming email is unverified (WP lets users change theirs without // confirmation), so it must not overwrite a linked customer's address. // That only changes via EmailClaimService or an agent edit. if ((int) $customer->user_id) { unset($customerData['email']); } if (!empty($customerData['user_id']) || !empty($customerData['remote_uid'])) { $customerData = array_filter($customerData); $customer->fill($customerData); $customer->save(); } return $customer; } /** * @return Model Collection */ public function tickets() { $foreign_key = self::$type . '_id'; $class = __NAMESPACE__ . '\Ticket'; return $this->hasMany( $class, $foreign_key, 'id' ); } /** * Look up by remote_uid, then user_id, then email. * * @param array $customerData * @return false|Customer */ public static function getCustomerFromData($customerData) { $remoteUid = Arr::get($customerData, 'remote_uid'); $email = Arr::get($customerData, 'email'); $userId = (int) Arr::get($customerData, 'user_id'); if ($remoteUid) { $customer = self::where('remote_uid', $remoteUid)->first(); if ($customer) { return $customer; } } // When a user id is given, don't fall back to email: a WP account's email // is unverified (the REST users endpoint skips confirmation), so it must // not reach another person's row. Email isn't unique, so take the oldest // match to keep replies threading onto the original record. if ($userId) { return self::where('user_id', $userId)->orderBy('id', 'ASC')->first() ?: false; } if ($email) { return self::where('email', $email)->orderBy('id', 'ASC')->first() ?: false; } return false; } /** * @return mixed */ public function getTicketCounts() { return Ticket::where('customer_id', $this->id)->count(); } /** * @return mixed */ public function getResponseCounts() { return Conversation::where('person_id', $this->id)->count(); } }