File: D:/HostingSpaces/slenders/slenders.nl/app/Komma/Shop/Orders/Kms/OrderService.php
<?php
namespace App\Komma\Shop\Orders\Kms;
use App\Komma\Kms\Core\Attributes\Attribute;
use App\Komma\Kms\Core\Sections\SectionService;
use App\Komma\Kms\Core\Sections\SectionTabItem;
use App\Komma\Shop\Categories\Kms\CategorizableInterface;
use App\Komma\Shop\Orders\Models\Order;
use App\Komma\Shop\Orders\OrderStatus;
use App\Komma\Shop\Orders\Product\OrderedProduct;
use App\Komma\Shop\Orders\ProductComposite\OrderedProductComposite;
use App\Komma\Shop\Orders\ProductGroup\OrderedProductGroup;
use App\Komma\Sites\SiteServiceInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection as BaseCollection;
class OrderService extends SectionService implements OrderServiceInterface
{
protected $sortable = false;
/** @var SiteServiceInterface $siteService */
protected $siteService;
function __construct()
{
$this->forModelName = Order::class;
$this->siteService = app(SiteServiceInterface::class);
parent::__construct();
}
/**
* Return a query builder to retrieve order orders by a certain status.
*
* @param string $status
* @return mixed
*/
public function ordersByStatus(string $status)
{
if(!OrderStatus::isValidItem($status)) throw new \InvalidArgumentException('The order status "'.$status.'" is invalid.');
return $this->forModelName::where('status', '=', $status);
}
/**
* Returns the order ids as a comma separated string for the CategorizableInterface implementation
*
* @param CategorizableInterface $model
* @return string Order ids, comma separated
*/
public function getOrderIdsForModel(CategorizableInterface $model): ?string
{
if(!$model->id) return null;
$idsCollection = $model->orders()->get(['order_id'])->map(function (Order $order) {
return $order->order_id;
});
$idString = implode(',', $idsCollection->toArray());
if($idString == "") return null;
return $idString;
}
/**
* Calculates the total order price and updates the orders total price.
* Then returns the order. Notice that this method does not save the order.
*
* The total price is in cents
*
* @param Order $order
* @return Order
*/
public function calculateTotalOrderPrice(Order $order): Order
{
$order->load([
'orderedProducts',
'orderedProductGroups.orderedProducts',
'orderedProductComposites.orderedGroups.orderedProducts'
]);
$productsPrice = $order->orderedProducts->map(function (OrderedProduct $orderedProduct) {
return $orderedProduct->price;
})->sum();
$productGroupsPrice = $order->orderedProductGroups->map(function (OrderedProductGroup $orderedProductGroup) {
return $orderedProductGroup->price;
})->sum();
$productCompositesPrice = $order->orderedProductComposites->map(function (
OrderedProductComposite $orderedProductComposite
) {
return $orderedProductComposite->price;
})->sum();
//Prices are with discounts taken into account. So we can simply sum them and we are good to go
$order->total_price = $productsPrice + $productGroupsPrice + $productCompositesPrice;
return $order;
}
/**
* Retrieves an array where the key names are field names, and value are the search values.
* And uses that array to search for orders. The found orders are returned as a QueryBuilder
* that holds the query to return all the found orders.
*
* @param array $input $input
* @return Builder
*/
public function search(array $input): Builder
{
/** @var Builder $ordersQueryBuilder */
$ordersQueryBuilder = $this->forModelName::query();
foreach ($input as $fieldName => $searchValue) {
if ($searchValue == '') {
continue;
}
switch ($fieldName) {
case 'order_number':
$ordersQueryBuilder->where('order_number', 'LIKE', '%'.$searchValue.'%');
break;
case 'first_name':
$ordersQueryBuilder->whereHas('customer', function (Builder $builder) use ($searchValue) {
$builder->where('first_name', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'last_name':
$ordersQueryBuilder->whereHas('customer', function (Builder $builder) use ($searchValue) {
$builder->where('last_name', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'email':
$ordersQueryBuilder->whereHas('invoiceAddress', function (Builder $builder) use ($searchValue) {
$builder->where('email', 'LIKE', '%' . $searchValue . '%');
})
->orWhereHas('shippingAddress', function (Builder $builder) use ($searchValue) {
$builder->where('email', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'street':
$ordersQueryBuilder->whereHas('invoiceAddress', function (Builder $builder) use ($searchValue) {
$builder->where('street', 'LIKE', '%' . $searchValue . '%');
})
->orWhereHas('shippingAddress', function (Builder $builder) use ($searchValue) {
$builder->where('street', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'house_number':
$ordersQueryBuilder->whereHas('invoiceAddress', function (Builder $builder) use ($searchValue) {
$builder->where('houseNumber', 'LIKE', '%' . $searchValue . '%');
})
->orWhereHas('shippingAddress', function (Builder $builder) use ($searchValue) {
$builder->where('houseNumber', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'postal_code':
$ordersQueryBuilder->whereHas('invoiceAddress', function (Builder $builder) use ($searchValue) {
$builder->where('postal_code', 'LIKE', '%' . $searchValue . '%');
})
->orWhereHas('shippingAddress', function (Builder $builder) use ($searchValue) {
$builder->where('postal_code', 'LIKE', '%' . $searchValue . '%');
});
break;
case 'status':
if ($searchValue == 'each') {
continue;
}
$ordersQueryBuilder = $ordersQueryBuilder->where('status', '=', $searchValue);
break;
}
}
return $ordersQueryBuilder;
}
/**
* Change the status for a collection of orders,
* save them, return them.
*
* @param iterable $orders
* @param string $status
* @return BaseCollection
*/
public function changeOrdersStatus(iterable $orders, string $status)
{
if(!self::iterableContainsOrders($orders)) throw new \InvalidArgumentException('The iterable does not exclusively contain orders.');
$collection = new BaseCollection();
foreach($orders as $order) {
/** @var Order $order */
$collection->push($this->changeOrderStatus($order, $status));
}
return $collection;
}
/**
* Change the status of an order
*
* @param Order $order
* @param string $status
* @return Order
*/
public function changeOrderStatus(Order $order, string $status)
{
if(!OrderStatus::isValidItem($status)) throw new \InvalidArgumentException('The given status is invalid. Use the OrderStatus enum for valid statuses');
//TODO. Maybe add some constraints to allow or disallow certain status updates
$order->status = $status;
$order->save();
return $order;
}
/**
* Returns true if the iteratable contains orders only. false if not.
*
* @param iterable $iterable
* @return bool
*/
public static function iterableContainsOrders(iterable $iterable):bool
{
foreach($iterable as $item) if(!is_a($item, Order::class)) return false;
return true;
}
/**
* Returns the latest x orders. Where x is a random number.
*
* @param $int
* @return Builder
*/
public function getLatest($int): Builder
{
/** @var Builder $ordersQueryBuilder */
$ordersQueryBuilder = $this->forModelName::query();
return $ordersQueryBuilder->latest('created_at')->limit($int);
}
/**
* This method will save an model
*
* @param Model $model or null
* @param Collection $sectionTabItems These must be filled with data. This is something you need to do yourself.
*
* @return mixed
*/
public function saveModel(Model $model = null, Collection $sectionTabItems): Model
{
$sectionTabItems->each(
function($sectionTabItem, $key) use($model, &$editedModel) {
/** @var Order $model */
/** @var SectionTabItem $sectionTabItem */
$attribute = $sectionTabItem->getAttribute();
$value = $attribute->getValue();
// if(is_a($attribute, Select::class)) dd($attribute); //debugging helper
$reference = $attribute->getsValueFromReference();
switch ($attribute->getsValueFrom()) {
case Attribute::ValueFromItself:
if($reference == 'invoice_postal_code') $model->invoice_postal_code = $value;
if($reference == 'invoice_city') $model->invoice_city= $value;
if($reference == 'invoice_street') $model->invoice_street= $value;
if($reference == 'shipping_postal_code') $model->shipping_postal_code= $value;
if($reference == 'shipping_city') $model->shipping_city= $value;
if($reference == 'shipping_street') $model->shipping_street= $value;
break;
}
}
);
$model->save();
parent::saveModel($model, $sectionTabItems);
return $model;
}
/**
* @param Collection $sectionTabItems A collection containing implementations AbstractSectionTabItem's
* @param Model $model
* @return Collection
*/
public function fillAttributesWithData(Collection $sectionTabItems, Model $model)
{
$filledAttributesCollection = parent::fillAttributesWithData($sectionTabItems, $model);
$sectionTabItems->each(
function ($sectionTabItem, $key) use ($model, $filledAttributesCollection) {
/** @var $sectionTabItem SectionTabItem */
/** @var Order $model */
if (!is_a($sectionTabItem->getAttribute(), Attribute::class)) throw new \InvalidArgumentException("One of the attributes in a AbstractSectionTabItem instance is not but must be an child instance of Attribute.");
$attribute = $sectionTabItem->getAttribute();
$valueReference = $sectionTabItem->getAttribute()->getsValueFromReference();
switch($sectionTabItem->getAttribute()->getsValueFrom()) {
case Attribute::ValueFromItself:
switch ($valueReference) {
case 'invoice_postal_code':
$attribute->setValue($model->invoice_postal_code);
break;
case 'invoice_city':
$attribute->setValue($model->invoice_city);
break;
case 'invoice_street':
$attribute->setValue($model->invoice_street);
break;
case 'shipping_postal_code':
$attribute->setValue($model->shipping_postal_code);
break;
case 'shipping_city':
$attribute->setValue($model->shipping_city);
break;
case 'shipping_street':
$attribute->setValue($model->shipping_street);
break;
}
break;
}
}
);
return $filledAttributesCollection;
}
}