File: D:/HostingSpaces/SBogers10/shop.komma.nl/app/Orders/Kms/OrderService.php
<?php
namespace App\Orders\Kms;
use App\Cart\ShoppingCartItem;
use App\Orders\Product\OrderedProduct;
use App\Vat\VatRateTotal;
use App\Vat\VatService;
use Illuminate\Database\Eloquent\Model;
use Komma\KMS\Core\ModelService;
use App\Categories\Kms\CategorizableInterface;
use App\Orders\Models\Order;
use App\Orders\OrderStatus;
use Komma\KMS\Sites\SiteServiceInterface;;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Illuminate\Support\Facades\URL;
class OrderService extends ModelService
{
protected $sortable = false;
/** @var SiteServiceInterface $siteService */
protected $siteService;
function __construct()
{
parent::__construct();
$this->setModelClassName(Order::class);
}
/**
* 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->modelClassName::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;
}
/**
* 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->modelClassName::query();
$ordersQueryBuilder->with('transactions');
foreach ($input as $fieldName => $searchValue) {
if ($searchValue == '') {
continue;
}
switch ($fieldName) {
case 'order_number':
$ordersQueryBuilder->where('order_number', 'LIKE', '%'.$searchValue.'%');
break;
case 'first_name':
$ordersQueryBuilder->where('first_name', 'LIKE', '%'.$searchValue.'%');
break;
case 'last_name':
$ordersQueryBuilder->where('last_name', 'LIKE', '%'.$searchValue.'%');
break;
case 'email':
$ordersQueryBuilder->where('email', 'LIKE', '%'.$searchValue.'%');
break;
case 'street':
$ordersQueryBuilder->where('shipping_street', 'LIKE', '%'.$searchValue.'%')
->orWhere('invoice_street', 'LIKE', '%'.$searchValue.'%');
break;
case 'house_number':
$ordersQueryBuilder->where('shipping_house_number', 'LIKE', '%'.$searchValue.'%')
->orWhere('invoice_house_number', 'LIKE', '%'.$searchValue.'%');
break;
case 'postal_code':
$ordersQueryBuilder->where('shipping_postal_code', 'LIKE', '%'.$searchValue.'%')
->orWhere('invoice_postal_code', 'LIKE', '%'.$searchValue.'%');
break;
case 'status':
if ($searchValue == 'each') continue 2;
$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;
}
public function destroyForModel(Model $model): Model
{
/** @var Order $model */
$model->orderedProducts()->delete();
$model->orderedGroups()->delete();
$model->orderedProductComposites()->delete();
$model->transactions()->delete();
$model->shipments()->delete();
return parent::destroyForModel($model);
}
/**
* 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->modelClassName::query();
return $ordersQueryBuilder->latest('created_at')->limit($int);
}
/**
* Generates an url that must lead to a page in which a user can view IF he still needs to
* pay for an order or not. If he needs to pay for it, this page must have a way to
* redirect the user to the PSP payment endpoint.
*
* @param Order $order
* @return string
*/
public function getManualPaymentUrl(Order $order)
{
return URL::signedRoute('orders.manual_payment', ['order' => $order]);
}
/**
* @param Order $order
* @return OrderedProduct|null
*/
public function getOrderedProductWithHighestVatPercentage(Order $order): ?OrderedProduct {
return $order->orderedProducts->reduce(function(?OrderedProduct $previousHighestOrderedProduct, OrderedProduct $currentOrderedProduct) {
if(!$previousHighestOrderedProduct) return $currentOrderedProduct;
return ($currentOrderedProduct->vat_percentage > $previousHighestOrderedProduct->vat_percentage) ? $currentOrderedProduct : $previousHighestOrderedProduct;
}, null);
}
/**
* @param $status
* @return Builder
*/
public function byStatus($status): Builder
{
$ordersQueryBuilder = $this->modelClassName::query();
if(is_string($status)) {
$ordersQueryBuilder->where('status', '=', $status);
} elseif(is_array($status)) {
$ordersQueryBuilder->whereIn('status', $status);
}
return $ordersQueryBuilder;
}
/**
* @return Builder
*/
public function nonFulfilled(): Builder
{
return $this->modelClassName::whereNotIn('status', [
OrderStatus::COMPLETED,
OrderStatus::SHIPPED,
OrderStatus::CANCELED
]);
}
}