HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/topswtw.komma.pro/app/KommaApp/Shop/Customers/CustomerService.php
<?php


namespace KommaApp\Shop\Customers;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use Komma\Kms\Products\Models\Product;
use KommaApp\Shop\Customers\Customer;
use KommaApp\Shop\Checkout\CheckoutSession\CheckoutSession;
use KommaApp\Shop\FormValidation\UpdateCustomerForm;
use KommaApp\Shop\Orders\Order;
use KommaApp\Shop\FormValidation\CustomerForm;
use KommaApp\Shop\FormValidation\FullCustomerForm;
use KommaApp\Shop\FormValidation\LoginForm;
use KommaApp\Shop\Mailers\CustomerMailer;
use TijsVerkoyen\CssToInlineStyles\Exception;

class CustomerService
{
    /**
     * @var FullCustomerForm
     */
    private $fullCustomerForm;

    /**
     * @var CustomerForm
     */
    private $customerForm;

    /**
     * @var Customer
     */
    private $customer;

    /**
     * @var CheckoutSession
     */
    private $checkoutSession;

    /**
     * @var LoginForm
     */
    private $loginForm;

    private $customerMailer;

    /**
     * @param Customer $customer
     * @param FullCustomerForm $fullCustomerForm
     * @param UpdateCustomerForm $updateCustomerForm
     * @param CustomerForm $customerForm
     * @param LoginForm $loginForm
     * @param CheckoutSession $checkoutSession
     * @param CustomerMailer $customerMailer
     */
    public function __construct(
        Customer $customer,
        FullCustomerForm $fullCustomerForm,
        UpdateCustomerForm $updateCustomerForm,
        CustomerForm $customerForm,
        LoginForm $loginForm,
        CheckoutSession $checkoutSession,
        CustomerMailer $customerMailer
    )
    {
        $this->fullCustomerForm = $fullCustomerForm;
        $this->updateCustomerForm = $updateCustomerForm;
        $this->customerForm = $customerForm;
        $this->customer = $customer;
        $this->checkoutSession = $checkoutSession;
        $this->loginForm = $loginForm;
        $this->customerMailer = $customerMailer;
    }

    public function updateCustomer(array $input, $page = 'customer')
    {
        //todo: this is not the way to go, the lang should be set in the contruct
        //Translate the messages
        $this->updateCustomerForm->translateMessages();


        //Not logged in  so new customer
        if (!$currentCustomer = $this->getLoggedInCustomer()) return $this->storeNewCustomer($input);

        // Validate the form
        if (!$this->updateCustomerForm->isValid($input))
            return \Redirect::back()
                ->withInput()
                ->withErrors($this->updateCustomerForm->errorMessages());
        if (!$currentCustomer = $this->getLoggedInCustomer())
            throw new \Exception('Update failed: Can\'t find current user.');

        // Prepare input data for update
        $input['username'] = $input['email'];

        if ($input['company'] == '') $input['company_vat'] = '';

        $input['title'] == 'mr' ? $input['gender'] = 'male' : $input['female'] = 'male';

        if ($input['password'] == '') {
            unset($input['password']);
        } else {
            $input['password'] = \Hash::make($input['password']);
        }

        // Save the customer
        $currentCustomer->fill($input);
        $currentCustomer->save();

        return \Redirect::to(\Shop::getPageService()->page($page)->route);
    }

    public function storeNewCustomerFromToken($orderToken, $password)
    {
        $order = Order::where('order_token', $orderToken)->first();
        $data = [
            'username' => $order->invoice_email,
            'email' => $order->invoice_email,
            'password' => $password,
            'password_confirmation' => $password,
            'gender' => $order->invoice_gender,
            'title' => $order->invoice_title,
            'first_name' => $order->invoice_first_name,
            'last_name' => $order->invoice_last_name,
            'name_insertion' => $order->invoice_name_insertion,
            'company' => $order->invoice_company,
            'company_vat' => $order->invoice_company_vat,
            'postal' => $order->invoice_postal,
            'city' => $order->invoice_city,
            'street' => $order->invoice_street,
            'house_number' => $order->invoice_house_number,
            'house_number_suffix' => $order->invoice_house_number_suffix,
            'country' => $order->invoice_country,
            'order_id' => $order->id,
        ];
        return $this->storeNewCustomer($data);
    }

    /**
     * @param array $input
     */
    public function storeNewCustomer(array $input)
    {
        $input['username'] = $input['email'];

        if (isset($input['company_vat'])) {
            $input['company_vat'] = strtoupper($input['company_vat']);
            $input['company_vat'] = preg_replace("/[^a-zA-Z0-9]/", "", $input['company_vat']);
        }
        //todo: this is not the wat to, the lang should be set in the contruct
        //Translate the messages
        $this->fullCustomerForm->translateMessages();

        // Validate the form
        if (!$this->fullCustomerForm->isValid($input)) {
            return \Redirect::back()
                ->withInput()
                ->withErrors($this->fullCustomerForm->errorMessages());
        }

        //TODO: Als een klant registreert, niet activeert, registreert activeert, kan hij meerdere accounts de hetzelfde email hebben
        if ($currentCustomer = Customer::where('email', $input['email'])->where('shop_id', \Shop::getId())->first()) {
            if ($this->customer->active == 1) {
                return \Lang::get('customer/create.error_user_exists');
            }
            $this->customer = $currentCustomer;
        }

        $input['shop_id'] = \Shop::getId();
        $input['password'] = \Hash::make($input['password']);
        $input['title'] == 'mr' ? $input['gender'] = 'male' : $input['female'] = 'male';

        do { // Ensure that validate_token is unique
            $input['validate_token'] = str_random(32);
        } while (!$this->customer->where('validate_token', $input['validate_token'])->get());

        $input['customer_number'] = Customer::getNewCustomerNumber();


        // Create Customer
        if (!$this->customer
            ->fill($input)
            ->save()
        ) {
            // Todo: With error / flash message
            return \Redirect::back()
                ->withInput();
        };

        if (isset($input['order_id'])) {
            $order = Order::find($input['order_id']);
            $order->customer()->associate($this->customer);
            $order->save();
        }

        try {
            $this->customerMailer->sendCustomerValidation($input);
        } catch (\Exception $e) {
            // Todo: With error / flash message
            return \Redirect::back();
        }

        return \View::make( viewPrefix() . 'customers.sendValidation', ['email' => $input['email']]);
    }


    /**
     * Duplicate of store new customer,
     * I don't want to change the flow a week befor live
     * Todo: fix this and make it in one function
     *
     * @param $input
     * @return mixed
     *
     */
    public function streamLinedStoreNewCustomer($input)
    {
        //Clean up the VAt if it is set
        if (isset($input['company_vat'])) {
            $input['company_vat'] = strtoupper($input['company_vat']);
            $input['company_vat'] = preg_replace("/[^a-zA-Z0-9]/", "", $input['company_vat']);
        }

        $input['shop_id'] = \Shop::getId();
        if (isset($input['password'])) {
            $input['password'] = \Hash::make($input['password']);
        }

        $input['title'] == 'mr' ? $input['gender'] = 'male' : $input['female'] = 'male';

        do { // Ensure that validate_token is unique
            $input['validate_token'] = str_random(32);
        } while (!$this->customer->where('validate_token', $input['validate_token'])->get());

        $input['customer_number'] = Customer::getNewCustomerNumber();

        if (!isset($input['username'])) {
            $input['username'] = $input['customer_number'];
        }
        $input['email'] = $input['customer_email'];
        // Create Customer
        if (!$this->customer
            ->fill($input)
            ->save()
        ) {

            return false;
        };
        return $this->customer;
    }


    public function activateCustomer($token)
    {
        if ($this->customer->where('validate_token', $token)->first() && $this->customer->where('validate_token', $token)->first()->active == 1) {
            return \View::make( viewPrefix() . 'customers.invalidLink');
        }
        if (!($customer = $this->customer->where('validate_token', $token)->first())) {
            return \View::make( viewPrefix() . 'customers.invalidLink');
        }
        $customer->active = 1;
        $customer->validate_token = null;
        $customer->save();
        return \View::make( viewPrefix() . 'customers.activated');
    }

    /**
     * @param $input
     */
    public function store($input)
    {
        if (isset($input['company_vat'])) {
            $input['company_vat'] = strtoupper($input['company_vat']);
            $input['company_vat'] = preg_replace("/[^a-zA-Z0-9]/", "", $input['company_vat']);
        }

        // Validate the form
        if (!$this->customerForm->isValid($input)) {
            return \Redirect::back()
                ->withErrors($this->customerForm->errorMessages());
        }

        // Create data array
        $data = [];

        // check for unique email
        if ($currentCustomer = Customer::where('email', $input['email'])->where('shop_id', \Shop::getId())->first()) {
            if ($this->customer->active == 1) {
                return \Lang::get('customer/create.error_user_exists');
            }
            $this->customer = $currentCustomer;
        }

        // Get user data from the checkoutSession
        /*
        foreach($this->checkoutSession->customer()->get() as $invoiceField => $value)
        {
            $dbKey = str_replace('invoice-','',$invoiceField);
            $dbKey = str_replace('-','_',$dbKey);

            $data[$dbKey] = $value;
        }
        */

        // Other data
        $data['shop_id'] = \Shop::getId();
        $data['username'] = $data['email'];

        $data['customer_number'] = Customer::getNewCustomerNumber();

        // Set password
        $this->customer->password = \Hash::make($input['password']);
        $this->customer->remember_token = $data['remember_token'];

        // Set validation token
        do { // Ensure that validate_token is unique
            $input['validate_token'] = str_random(32);
        } while (!$this->customer->where('validate_token', $input['validate_token'])->get());
        $data['validate_token'] = $this->customer->validate_token = $input['validate_token'];

        // Mass assign data and save order
        if (!$this->customer
            ->fill($data)
            ->save()
        ) {
            // Todo message
            // Bevestigings mail
            return \Redirect::back();
        };

        if (isset($input['order_id'])) {
            $order = Order::find(\Input::get('order_id'));
            $order->customer()->associate($this->customer);
            $order->save();
        }

        try {
            $this->customerMailer->sendCustomerValidation($input);
        } catch (\Exception $e) {
            // Todo: With error / flash message
            return \Redirect::back();
        }

        return \View::make( viewPrefix() . 'customers.sendValidation', ['email' => $input['email']]);
    }

    public function isLoggedIn()
    {
        return \Auth::customer()->check();
    }

    public function authorizeLogin($input, $pageSlug = 'customer', $anchor = '')
    {

        // Is the form valid
        $messages = [
            'email.exists' => \Lang::get('customer/login.email_exists'),
            'email.activated' => \Lang::get('customer/login.email_activated'),
        ];
        $this->loginForm->setMessages($messages);
        if (!$this->loginForm->isValid($input)) {
            return \Redirect::back()
                ->withInput($input)
                ->withErrors(['loginError' => \Lang::get('customer/login.loginError')]);
        }

        $input['active'] = 1;
        $input['shop_id'] = \Shop::getId();

        // Does the user exists
        if (!\Auth::customer()->attempt($input)) {
            return \Redirect::back()
                ->withInput($input)
                ->withErrors(['loginError' => \Lang::get('customer/login.loginError')]);
            // Todo errors
        }

        // Redirect to checkout data
        $this->checkoutSession->customer()->fillAfterLogin();
        return \Redirect::to(\Shop::getPageService()->page($pageSlug)->route);
    }

    public function getLoggedInCustomer()
    {
        if (!$this->isLoggedIn()) {
            return null;
        }
        return \Auth::customer()->get();
    }

    public function checkEmailAddress($email)
    {
        $customer = $this->customer
            ->where('email', $email)
            ->where('shop_id', \Shop::getId())
            ->where('active', 1)
            ->first();
        if ($customer == null) return false;
        return true;
    }

    public function getReminderService()
    {
        return $this->reminderService;
    }

    public function getCustomerByEmail($email)
    {
        $customer = Customer::
        where('email', '=', $email)
            ->where('shop_id', '=', \Shop::getId())
            ->where('active', '=', 1)
            ->first();
        return $customer;
    }

    /**
     * In this Method we will activate the account based on an order token
     * We'll check if there is an customer for the order and link this
     * We are going to activate this account and set token to null
     *
     * @param $orderToken
     * @param $password
     * @return array|bool
     *
     */
    public function LinkAccountFromOrder($orderToken, $password)
    {
        //Load order by Token
        if (!$order = \App::make('KommaApp\Shop\Orders\OrderService')->getOrderByToken($orderToken)) {
            //No order found, return with error message
        }
        //load customer from order
        if (!$customer = $order->customer) {
            //No customer found, this is normaly not possible todo: create customer from token?
            return ['error' => 'Oops Something went wrong'];
        }

        $this->activateCustomerAndSetPassword($customer, $password);

        return true;
    }

    /**
     * In this Method we will activate the account based on an order token
     * We'll check if there is an customer for the order and link this
     * We are going to activate this account and set token to null
     *
     * @param $orderToken
     * @param $password
     * @return array|bool
     *
     */
    public function linkAccountFromMaintenanceOrder($maintenanceOrder, $password)
    {
        //Load the customer from the order
        if (!$customer = $maintenanceOrder->customer) {
            //No customer found, this is normally not possible
            return ['error' => 'Oops Something went wrong'];
        }
        //Activate the customer and set the password
        $this->activateCustomerAndSetPassword($customer, $password);

        return true;

    }

    public function activateCustomerAndSetPassword($customer, $password)
    {
        //Set password
        $customer->password = \Hash::make($password);
        //Set username, use the customer email (is our default)
        $customer->username = $customer->email;
        //set account active
        $customer->active = 1;
        //Set validate_token to null
        $customer->validate_token = null;

        //save the customer
        $customer->save();
    }

    public function validateCustomerForMaintenance()
    {

        $customerInput = $this->prepareMaintenanceCustomerInput();

        //Edit the validation rules
        unset($this->fullCustomerForm->rules['company']);
        unset($this->fullCustomerForm->rules['username']);
        unset($this->fullCustomerForm->rules['password']);
        unset($this->fullCustomerForm->rules['email']);
        unset($this->fullCustomerForm->rules['company_vat']);

        $this->fullCustomerForm->rules['maintenance_type'] = 'required';
        $this->fullCustomerForm->rules['telephone'] = 'required';
        $this->fullCustomerForm->rules['customer_email'] = 'required|email';
        $this->fullCustomerForm->rules['agree'] = 'required';

        $validator = Validator::make($customerInput, $this->fullCustomerForm->rules);

        if ($validator->fails()) {
            return \Redirect::back()->withErrors($validator)->withInput();
        }

        return false;

    }

    public function prepareMaintenanceCustomerInput()
    {
        $customerInput = \Input::all();
        $customerInput['company'] = null;
        $customerInput['company_vat'] = null;

        return $customerInput;
    }

    public function validateLogin($input)
    {
        $rules = [
            'email' => 'required|exists:customers,email|activated',
            'password' => 'required',
        ];

        $messages = [
            'email.exists' => \Lang::get('customer/login/email.exists'),
            'email.activated' => \Lang::get('customer/login/email.activated'),
        ];

        $validator = Validator::make($input, $rules, $messages);

        if ($validator->fails()) return \Redirect::back()->withInput()->withErrors($validator);

    }

    public function mergeDuplicateCustomers()
    {
        set_time_limit(0);
//        $mergeOnAttributes = ['email', 'postal'];
        $mergeOnAttributes = ['email'];

        $duplicateUsersArray = $this->findDuplicateCustomersByCertainAttributes($mergeOnAttributes);

        $total = count($duplicateUsersArray);
        $count = 1;
        foreach ($duplicateUsersArray as $duplicateUserReference => $duplicateUserIds) {
            $customerIdToMergeInto = array_shift($duplicateUserIds);

            $customerToMergeInto = Customer::find($customerIdToMergeInto);
            $customersToMerge = Customer::whereIn('id', $duplicateUserIds)->get();

            echo '<br>Merging customers with id\'s "'.implode(', ', $duplicateUserIds).'" into user with id "'.$customerIdToMergeInto.'"<br/>';

            echo 'Merging orders<br>';
            $this->moveCustomersOrdersToOtherCustomer($customersToMerge, $customerToMergeInto);

            echo 'Merging maintenance orders<br>';
            $this->moveCustomersMaintenanceOrdersToOtherCustomer($customersToMerge, $customerToMergeInto);

            echo 'Merging attributes<br>';
            $this->mergeCustomersIntoCustomer($customersToMerge, $customerToMergeInto);

            echo 'Customer '.$count.' of '.$total.' done processing.<br><br>';
            $count++;
        }

        echo "done";
    }

    /**
     * Put a string into a predictable state
     *
     * @param string $value
     * @return string
     */
    public function normalize($value) {
        $value = trim($value);
        $value = str_replace(' ', '', $value);
        $value = strtolower($value);
        return $value;
    }

    /**
     * Returns an array of arrays that contain duplicate customers ids. The ids are sorted descending.
     *
     * @param array $duplicateAttributes. The attributes to search duplicates with. For example: [email, postal]
     * @return null
     */
    public function findDuplicateCustomersByCertainAttributes($duplicateAttributes) //gerrit@altac.nl
    {
        $columnNames = $duplicateAttributes;
        if(!in_array('id', $columnNames, true)) $columnNames[] = 'id';

        $customers = Customer::orderBy('id', 'DESC')->get($columnNames);

        $duplicateCustomers = [];

        $customers->each(function($customer) use(&$duplicateCustomers, $duplicateAttributes) {
            $attributeValues = [];
            foreach ($duplicateAttributes as $duplicateAttribute) $attributeValues[] = $customer->$duplicateAttribute;
            $currentCustomerAttributesString = $this->getAttributesString($attributeValues);

            if(!isset($duplicateCustomers[$currentCustomerAttributesString])) $duplicateCustomers[$currentCustomerAttributesString] = [];
            $duplicateCustomers[$currentCustomerAttributesString][] = $customer->id;
        });

        foreach($duplicateCustomers as $key => $duplicateCustomerIds)
        {
            if(count($duplicateCustomerIds) <= 1) unset($duplicateCustomers[$key]);
        }

        return $duplicateCustomers;
    }

    /**
     * converts an array of values to a string. Formatting the values in a way they are easy to compare.
     *
     * @param $attributeValues
     * @return string
     */
    private function getAttributesString($attributeValues)
    {
        foreach($attributeValues as $index => $attributeValue)
        {
            $attributeValues[$index] = $this->normalize($attributeValue);
        }

        return implode('|', $attributeValues);
    }

    /**
     * Gets an array of attribute names and values for a customer and returns a customer if it exists. null otherwise
     *
     * @param $attributesAndValues
     * @return null|Customer
     */
    public function getCustomerIfExists($attributesAndValues)
    {
        $columnNames = array_keys($attributesAndValues);
        if(!in_array('id', $columnNames, true)) $columnNames[] = 'id';

        $customers = Customer::orderBy('id', 'DESC')->get($columnNames);
        foreach($customers as $customer)
        {
            $found = true;
            foreach($attributesAndValues as $expectedAttribute => $expectedValue)
            {
                if($expectedAttribute == "postal") {
                    $expectedValue = $this->normalize($expectedValue);
                    $customer->postal = $this->normalize($customer->postal);
                }

                if($customer->$expectedAttribute !== $expectedValue) {
                    $found = false;
                    break;
                }
            }

            if($found) return $customer;
        }

        return null;
    }

    /**
     * Merges a collection of customers into a customer and deletes the mergedCustomers if the last parameter is set to tree
     *
     * @param Collection $customers
     * @param Customer $customerToMergeInto
     * @param bool $deleteMergedCustomers
     * @return string $log string
     */
    public function mergeCustomersIntoCustomer($customers, $customerToMergeInto, $deleteMergedCustomers = true)
    {
        $customers->each(function($customerToMerge) use (&$customerToMergeInto, &$log, $deleteMergedCustomers) {
            $attributes = $customerToMerge->getAttributes();

            $excludedAttributes = ['password', 'active','created_at', 'updated_at'];
            foreach($attributes as $attributeName => $value)
            {
                //Merge attributes
                if(in_array($attributeName, $excludedAttributes, true)) break;
                if($customerToMerge->$attributeName !== '' && $customerToMerge->$attributeName !== null && ($customerToMergeInto->$attributeName == "" || $customerToMergeInto->$attributeName == null)) {
                    $customerToMergeInto->$attributeName = $value;
                    echo 'Merged "'.$attributeName.'" value "'.$value.'" of customer "'.$customerToMerge->id.'" into customer with id: '.$customerToMergeInto->id.'<br>';
                }
            }

            if($deleteMergedCustomers) $customerToMerge->delete();
        });

        $customerToMergeInto->save();
    }

    public function moveCustomersOrdersToOtherCustomer($sourceCustomerCollection, $customerToMergeInto)
    {
        $sourceCustomerCollection->each(function($customerToMerge) use (&$customerToMergeInto, &$log) {
            /** @var Customer $customerToMerge */
            /** @var Customer $customerToMergeInto */

            $orders = $customerToMerge->orders()->get(['id']);
            $orders->each(function ($order) use (&$customerToMergeInto, &$customerToMerge, &$log) {
                /** @var Order $order */
                echo 'Moving order with id "'.$order->id.'" from customer with id "'.$customerToMerge->id.'" to customer with id "'.$customerToMergeInto->id.'"<br/>';

                $order->customer()->associate($customerToMergeInto);
                $order->save();
            });
        });
    }

    public function moveCustomersMaintenanceOrdersToOtherCustomer($sourceCustomerCollection, $customerToMergeInto)
    {
        $sourceCustomerCollection->each(function($customerToMerge) use (&$customerToMergeInto, &$log) {
            /** @var Customer $customerToMerge */
            /** @var Customer $customerToMergeInto */

            $orders = $customerToMerge->maintenanceOrders()->get(['id']);
            $orders->each(function ($order) use (&$customerToMergeInto, &$customerToMerge, &$log) {
                /** @var Order $order */
                echo 'Moving maintenanceOrder with id "'.$order->id.'" from customer with id "'.$customerToMerge->id.'" to customer with id "'.$customerToMergeInto->id.'"<br/>';

                $order->customer()->associate($customerToMergeInto);
                $order->save();
            });
        });
    }
}