Skip to content

Commit e3df162

Browse files
[6.x] Query Builders (#973)
* Implement query builder for orders * Refactor telemetry queries * Refactor purge cart orders command * Refactor mollie webhook * Fix styling * Use query builder for all method * Add query method to Order repository contract * Product query builder * Refactor product queries * Customer query builder * Take notes about tests needing refactoring * IDs should be settable in orders and products before saving * Add missing tests for data repositories * ensure entry query builders only query entries of their respective collections * wip * This can only be compatible with one driver * Refactor order number generation query * These can't be refactored Since the "current repository" in this case would be the eloquent one so Customer::query() wouldn't pick up old customers. * Fix styling * Add whereStatusLogDate method to order query builders * Cover our custom methods in order query builders * Refactor orders chart query * Refactor recent orders query * Refactor update class references query * wip * wip * Fix styling * This'll have to do * Fix styling * Make key names dynamic * Fix *some* tests * Lets try something --------- Co-authored-by: duncanmcclean <duncanmcclean@users.noreply.github.com>
1 parent ea5d522 commit e3df162

34 files changed

+884
-516
lines changed

docs/payment-gateways/custom-gateways.md

+2-10
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,9 @@ public function webhook(Request $request)
120120
$payment = $this->mollie->payments->get($mollieId);
121121

122122
if ($payment->status === MolliePaymentStatus::STATUS_PAID) {
123-
$order = collect(OrderFacade::all())
124-
->filter(function ($entry) use ($mollieId) {
125-
return isset($entry->data()->get('mollie')['id'])
126-
&& $entry->data()->get('mollie')['id']
127-
=== $mollieId;
128-
})
129-
->map(function ($entry) {
130-
return OrderFacade::find($entry->id());
131-
})
123+
$order = OrderFacade::query()
124+
->where('data->mollie->id', $request->get('id'))
132125
->first();
133-
}
134126

135127
if (! $order) {
136128
throw new OrderNotFound("Order related to Mollie transaction [{$mollieId}] could not be found.");

src/Console/Commands/PurgeCartOrdersCommand.php

+10-29
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22

33
namespace DoubleThreeDigital\SimpleCommerce\Console\Commands;
44

5-
use DoubleThreeDigital\SimpleCommerce\Orders\EloquentOrderRepository;
6-
use DoubleThreeDigital\SimpleCommerce\Orders\EntryOrderRepository;
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Order;
76
use DoubleThreeDigital\SimpleCommerce\Orders\OrderStatus;
8-
use DoubleThreeDigital\SimpleCommerce\SimpleCommerce;
97
use Illuminate\Console\Command;
8+
use Illuminate\Support\Carbon;
109
use Statamic\Console\RunsInPlease;
11-
use Statamic\Facades\Entry;
1210

1311
class PurgeCartOrdersCommand extends Command
1412
{
@@ -22,32 +20,15 @@ public function handle()
2220
{
2321
$this->info('Cleaning up..');
2422

25-
if ($this->isOrExtendsClass(SimpleCommerce::orderDriver()['repository'], EntryOrderRepository::class)) {
26-
Entry::whereCollection(SimpleCommerce::orderDriver()['collection'])
27-
->where('order_status', OrderStatus::Cart->value)
28-
->filter(function ($entry) {
29-
return $entry->date()->isBefore(now()->subDays(14));
30-
})
31-
->each(function ($entry) {
32-
$this->line("Deleting Order: {$entry->id()}");
33-
34-
$entry->delete();
35-
});
36-
}
37-
38-
if ($this->isOrExtendsClass(SimpleCommerce::orderDriver()['repository'], EloquentOrderRepository::class)) {
39-
$orderModelClass = SimpleCommerce::orderDriver()['model'];
40-
41-
(new $orderModelClass)
42-
->query()
43-
->where('order_status', OrderStatus::Cart->value)
44-
->where('created_at', '<', now()->subDays(14))
45-
->each(function ($model) {
46-
$this->line("Deleting Order: {$model->id}");
47-
48-
$model->delete();
23+
Order::query()
24+
->whereOrderStatus(OrderStatus::Cart)
25+
->where('updated_at', '<=', Carbon::now()->subDays(14)->timestamp)
26+
->chunk(100, function ($orders) {
27+
$orders->each(function ($order) {
28+
$this->line("Deleting Order: {$order->id()}");
29+
$order->delete();
4930
});
50-
}
31+
});
5132
}
5233

5334
protected function isOrExtendsClass(string $class, string $classToCheckAgainst): bool

src/Console/Commands/UpdateClassReferences.php

+19-48
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22

33
namespace DoubleThreeDigital\SimpleCommerce\Console\Commands;
44

5-
use DoubleThreeDigital\SimpleCommerce\Orders\EloquentOrderRepository;
6-
use DoubleThreeDigital\SimpleCommerce\Orders\EntryOrderRepository;
7-
use DoubleThreeDigital\SimpleCommerce\Orders\OrderModel;
8-
use DoubleThreeDigital\SimpleCommerce\SimpleCommerce;
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Order;
96
use Illuminate\Console\Command;
107
use Statamic\Console\RunsInPlease;
11-
use Statamic\Facades\Collection;
128

139
class UpdateClassReferences extends Command
1410
{
@@ -22,50 +18,25 @@ public function handle()
2218
{
2319
$this->info('Updating class references...');
2420

25-
if ($this->isOrExtendsClass(SimpleCommerce::orderDriver()['repository'], EntryOrderRepository::class)) {
26-
Collection::find(SimpleCommerce::orderDriver()['collection'])
27-
->queryEntries()
28-
->where('gateway', '!=', null)
29-
->chunk(50, function ($orders) {
30-
$orders->each(function ($entry) {
31-
// When the gateway reference is still a class, change it to the handle.
32-
if (class_exists($entry->get('gateway')['use'])) {
33-
$entry->set('gateway', array_merge($entry->get('gateway'), [
34-
'use' => $entry->get('gateway')['use']::handle(),
35-
]))->saveQuietly();
36-
}
37-
38-
// When the shipping method reference is still a class, change it to the handle.
39-
if ($entry->has('shipping_method') && class_exists($entry->get('shipping_method'))) {
40-
$entry->set('shipping_method', $entry->get('shipping_method')::handle())->saveQuietly();
41-
}
42-
});
43-
});
44-
}
45-
46-
if ($this->isOrExtendsClass(SimpleCommerce::orderDriver()['repository'], EloquentOrderRepository::class)) {
47-
OrderModel::query()
48-
->where('gateway', '!=', null)
49-
->chunk(50, function ($orders) {
50-
$orders->each(function ($order) {
51-
// When the gateway reference is still a class, change it to the handle.
52-
if (class_exists($order->gateway['use'])) {
53-
$order->gateway = array_merge($order->gateway, [
54-
'use' => $order->gateway['use']::handle(),
55-
]);
56-
57-
$order->saveQuietly();
58-
}
59-
60-
// When the shipping method reference is still a class, change it to the handle.
61-
if (isset($order->data['shipping_method']) && class_exists($order->data['shipping_method'])) {
62-
$order->data['shipping_method'] = $order->data['shipping_method']::handle();
63-
64-
$order->saveQuietly();
65-
}
66-
});
21+
Order::query()
22+
->where('gateway', '!=', null)
23+
->chunk(50, function ($orders) {
24+
$orders->each(function ($order) {
25+
// When the gateway reference is still a class, change it to the handle.
26+
if ($order->gateway() && class_exists($order->gateway()['use'])) {
27+
$order->gateway(array_merge($order->gateway(), [
28+
'use' => $order->gateway()['use']::handle(),
29+
]));
30+
31+
$order->save();
32+
}
33+
34+
// When the shipping method reference is still a class, change it to the handle.
35+
if ($order->has('shipping_method') && class_exists($order->get('shipping_method'))) {
36+
$order->set('shipping_method', $order->get('shipping_method')::handle())->saveQuietly();
37+
}
6738
});
68-
}
39+
});
6940
}
7041

7142
protected function isOrExtendsClass(string $class, string $classToCheckAgainst): bool

src/Contracts/CustomerRepository.php

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ interface CustomerRepository
66
{
77
public function all();
88

9+
public function query();
10+
911
public function find($id): ?Customer;
1012

1113
public function findByEmail(string $email): ?Customer;

src/Contracts/OrderRepository.php

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ interface OrderRepository
66
{
77
public function all();
88

9+
public function query();
10+
911
public function find($id): ?Order;
1012

1113
public function make(): Order;

src/Contracts/ProductRepository.php

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ interface ProductRepository
66
{
77
public function all();
88

9+
public function query();
10+
911
public function find($id): ?Product;
1012

1113
public function make(): Product;

src/Customers/EloquentCustomerRepository.php

+12-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ public function __construct()
2222

2323
public function all()
2424
{
25-
return (new $this->model)->all()->transform(fn ($model) => $this->fromModel($model));
25+
return $this->query()->get();
26+
}
27+
28+
public function query()
29+
{
30+
return app(EloquentQueryBuilder::class, [
31+
'builder' => (new $this->model)->query(),
32+
]);
2633
}
2734

2835
public function find($id): ?Customer
@@ -38,16 +45,16 @@ public function find($id): ?Customer
3845

3946
public function findByEmail(string $email): ?Customer
4047
{
41-
$model = (new $this->model)->query()->firstWhere('email', $email);
48+
$customer = $this->query()->where('email', $email)->first();
4249

43-
if (! $model) {
50+
if (! $customer) {
4451
throw new CustomerNotFound("Customer [{$email}] could not be found.");
4552
}
4653

47-
return $this->fromModel($model);
54+
return $customer;
4855
}
4956

50-
protected function fromModel($model)
57+
public function fromModel($model)
5158
{
5259
return app(Customer::class)
5360
->resource($model)
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace DoubleThreeDigital\SimpleCommerce\Customers;
4+
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Customer;
6+
use Statamic\Query\EloquentQueryBuilder as QueryEloquentQueryBuilder;
7+
8+
class EloquentQueryBuilder extends QueryEloquentQueryBuilder
9+
{
10+
protected function transform($items, $columns = ['*'])
11+
{
12+
return $items->map(fn ($item) => Customer::fromModel($item));
13+
}
14+
15+
protected function column($column)
16+
{
17+
if ($column === 'id') {
18+
return $this->builder->getModel()->getKeyName();
19+
}
20+
21+
return $column;
22+
}
23+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace DoubleThreeDigital\SimpleCommerce\Customers;
4+
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Customer;
6+
use Statamic\Auth\Eloquent\UserQueryBuilder;
7+
8+
class EloquentUserQueryBuilder extends UserQueryBuilder
9+
{
10+
protected function transform($items, $columns = ['*'])
11+
{
12+
return $items->map(fn ($item) => Customer::fromModel($item));
13+
}
14+
}

src/Customers/EntryCustomerRepository.php

+9-5
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ public function __construct()
2121

2222
public function all()
2323
{
24-
return Entry::query()
25-
->where('collection', $this->collection)
26-
->get()
27-
->transform(fn ($entry) => $this->fromEntry($entry));
24+
return $this->query()->get();
25+
}
26+
27+
public function query()
28+
{
29+
return app(EntryQueryBuilder::class, [
30+
'store' => app('stache')->store('entries'),
31+
])->where('collection', $this->collection);
2832
}
2933

3034
public function find($id): ?Customer
@@ -52,7 +56,7 @@ public function findByEmail(string $email): ?Customer
5256
return $this->fromEntry($entry);
5357
}
5458

55-
protected function fromEntry($entry)
59+
public function fromEntry($entry)
5660
{
5761
return app(Customer::class)
5862
->resource($entry)

src/Customers/EntryQueryBuilder.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace DoubleThreeDigital\SimpleCommerce\Customers;
4+
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Customer;
6+
use Statamic\Stache\Query\EntryQueryBuilder as QueryEntryQueryBuilder;
7+
8+
class EntryQueryBuilder extends QueryEntryQueryBuilder
9+
{
10+
public function get($columns = ['*'])
11+
{
12+
$get = parent::get($columns);
13+
14+
return $get->map(fn ($entry) => Customer::fromEntry($entry));
15+
}
16+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace DoubleThreeDigital\SimpleCommerce\Customers;
4+
5+
use DoubleThreeDigital\SimpleCommerce\Facades\Customer;
6+
use Statamic\Stache\Query\UserQueryBuilder;
7+
8+
class StacheUserQueryBuilder extends UserQueryBuilder
9+
{
10+
public function get($columns = ['*'])
11+
{
12+
$get = parent::get($columns);
13+
14+
return $get->map(fn ($item) => Customer::fromUser($item));
15+
}
16+
}

src/Customers/UserCustomerRepository.php

+19-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,24 @@ class UserCustomerRepository implements RepositoryContract
1212
{
1313
public function all()
1414
{
15-
return User::all()->transform(fn ($user) => $this->fromUser($user));
15+
return $this->query()->get();
16+
}
17+
18+
public function query()
19+
{
20+
// Statamic users can live in the database OR in the stache, so we have
21+
// to extend both query builders then use the relevant one.
22+
if ($this->isUsingEloquentUsers()) {
23+
$usersModel = config('auth.providers.users.model');
24+
25+
return app(EloquentUserQueryBuilder::class, [
26+
'builder' => (new $usersModel)->query(),
27+
]);
28+
}
29+
30+
return app(StacheUserQueryBuilder::class, [
31+
'store' => app('stache')->store('users'),
32+
]);
1633
}
1734

1835
public function find($id): ?Customer
@@ -37,7 +54,7 @@ public function findByEmail(string $email): ?Customer
3754
return $this->fromUser($user);
3855
}
3956

40-
protected function fromUser($user)
57+
public function fromUser($user)
4158
{
4259
return app(Customer::class)
4360
->resource($user)

0 commit comments

Comments
 (0)