Skip to content

Commit

Permalink
Merge branch '1.x' into patch-2
Browse files Browse the repository at this point in the history
  • Loading branch information
wychoong authored Sep 18, 2024
2 parents 4ebe4f1 + 4aad66d commit 60c11ff
Show file tree
Hide file tree
Showing 371 changed files with 4,907 additions and 1,918 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/fix-code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.ACCESS_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}
- name: Set up PHP
uses: shivammathur/setup-php@v2
Expand All @@ -26,5 +26,8 @@ jobs:
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_user_name: "GitHub Action"
commit_user_email: "[email protected]"
commit_author: "Author <[email protected]>"
commit_message: >
chore: fix code style
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"spatie/laravel-blink": "^1.7",
"spatie/laravel-medialibrary": "^11.0.0",
"spatie/laravel-permission": "^6.4",
"spatie/php-structure-discoverer": "^2.0",
"stripe/stripe-php": "^14.4",
"technikermathe/blade-lucide-icons": "^v3.0"
},
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export default defineConfig({
{text: 'Panel', link: '/admin/extending/panel'},
{text: 'Pages', link: '/admin/extending/pages'},
{text: 'Resources', link: '/admin/extending/resources'},
{text: 'Relation Managers', link: '/admin/extending/relation-managers'},
{text: 'Order Management', link: '/admin/extending/order-management'}
]
}
Expand Down
32 changes: 32 additions & 0 deletions docs/admin/extending/relation-managers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Extending Relation Managers

## MyCustomerGroupPricingRelationManagerExtension

An example of extending the CustomerGroupPricingRelationManager

```php
class MyCustomerGroupPricingRelationManagerExtension extends \Lunar\Admin\Support\Extending\RelationManagerExtension
{
public function extendForm(\Filament\Forms\Form $form): \Filament\Forms\Form
{
return $form->schema([
...$form->getComponents(withHidden: true),

\Filament\Forms\Components\TextInput::make('custom_column')
]);
}

public function extendTable(\Filament\Tables\Table $table): \Filament\Tables\Table
{
return $table->columns([
...$table->getColumns(),
\Filament\Tables\Columns\TextColumn::make('product_code')
]);
}
}

// Typically placed in your AppServiceProvider file...
LunarPanel::extensions([
\Lunar\Admin\Filament\Resources\ProductResource\RelationManagers\CustomerGroupPricingRelationManager::class => MyCustomerGroupPricingRelationManagerExtension::class,
]);
```
255 changes: 66 additions & 189 deletions docs/core/extending/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,238 +8,130 @@ Lunar provides a number of Eloquent Models and quite often in custom application
We highly suggest using your own Eloquent Models to add additional data, rather than trying to change fields on the core Lunar models.
:::

## Extendable Models
All Lunar models are now extendable.
This means you can now add your own functionality or change out existing core model behaviour using your own model implementations.
## Replaceable Models
All Lunar models are replaceable, this means you can instruct Lunar to use your own custom model, throughout the ecosystem, using dependency injection.

### Registration:

### Registration
We recommend registering your own models for your application within the boot method of your Service Provider.
When registering your models, you will need to set the Lunar core model as the key and then your own model implementation as the value.

Here is an example below where we are extending 10 core models from your main AppServiceProvider:
When registering your models, you will need to set the Lunar model's contract as the first argument then your own model implementation for the second.

```php
use Lunar\Models\Product;
use Lunar\Models\ProductVariant;
use Lunar\Models\ProductOption;
use Lunar\Models\ProductOptionValue;
use Lunar\Models\Collection;
use Lunar\Models\Customer;
use Lunar\Models\Cart;
use Lunar\Models\CartLine;
use Lunar\Models\Order;
use Lunar\Models\OrderLine;

```php
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$models = collect([
Product::class => \App\Models\Product::class,
ProductVariant::class => \App\Models\ProductVariant::class,
ProductOption::class => \App\Models\ProductOption::class,
ProductOptionValue::class => \App\Models\ProductOptionValue::class,
Collection::class => \App\Models\Collection::class,
Customer::class => \App\Models\Customer::class,
Cart::class => \App\Models\Cart::class,
CartLine::class => \App\Models\CartLine::class,
Order::class => \App\Models\Order::class,
OrderLine::class => \App\Models\OrderLine::class,
]);

ModelManifest::register($models);
\Lunar\Facades\ModelManifest::replace(
\Lunar\Models\Contracts\Product::class,
\App\Model\Product::class,
);
}
```

### Swap Implementation
You can override the model implementation at any time by calling the swap method on the core model.
When you call the swap method this will update the key value pair for the registered model. If you need to go back to the previous implementation then simply call the swap method again passing through your registered implementation.
#### Registering multiple Lunar models.

```php
namespace App\Models;

use Lunar\Models\ProductVariant;
If you have multiple models you want to replace, instead of manually replacing them one by one, you can specify a directory for Lunar to look in for Lunar models to use.
This assumes that each model extends its counterpart model i.e. `App\Models\Product` extends `Lunar\Models\Product`.

class ProductSwapModel extends \Lunar\Models\Product
```php
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
/**
* This will return the default variant for the product.
*
* @return bool
*/
public function defaultVariant(): ProductVariant
{
return $this->variants->first();
}
\Lunar\Facades\ModelManifest::addDirectory(
__DIR__.'/../Models'
);
}
```

```php
$product = \Lunar\Models\Product::find(1);

// This will swap out the registered implementation.
$product->swap(\App\Models\ProductSwapModel::class);
### Route binding

// You can now call this new method
$default = $product->defaultVariant();
Route binding is supported for your own routes and simply requires the relevant contract class to be injected.

// Swap again to go back to your original implementation or perhaps define a new one.
$product->swap(\App\Models\Product::class);
```php
Route::get('products/{id}', function (\Lunar\Models\Contracts\Product $product) {
$product; // App\Models\Product
});
```

### Examples
Here are some example simple use cases of extending the core models.
You are required to extend the core model `Lunar\Models\[Model]` in order for the relationships to function correctly.
### Relationship support

#### Example 1 - Adding static method (ProductOption Model)
If you replace a model which is used in a relationship, you can easily get your own model back via relationship methods. Assuming we want to use our own instance of `App\Models\ProductVariant`.

```php
namespace App\Models;

use Illuminate\Support\Collection;

class ProductOption extends \Lunar\Models\ProductOption
// In our service provider.
public function boot()
{
public static function getSizes(): Collection
{
return static::whereHandle('size')->first()->values;
}
\Lunar\Facades\ModelManifest::replace(
\Lunar\Models\Contracts\ProductVariant::class,
\App\Model\ProductVariant::class,
);
}
```
In this example you can access the static method via `\Lunar\Models\ProductOption::getSizes()`
Note: Static methods will not allow you to jump to the function declaration.
As a workaround simply add @see inline docblock:

```php
`/** @see \App\Models\ProductOption::getSizesStatic() */`
$newStaticMethod = \Lunar\Models\ProductOption::getSizesStatic();
```

#### Example 2 - Overriding trait method (Product Model)
// Somewhere else in your code...

```php
namespace App\Models;
$product = \Lunar\Models\Product::first();
$product->variants->first(); // App\Models\ProductVariant
```

use App\Concerns\SearchableTrait;
### Static call forwarding

class Product extends \Lunar\Models\Product
{
use SearchableTrait;
}
```
Note: shouldBeSearchable could also be overridden by adding directly to the Product class above.
In this example we are showing you how the core model can be made aware of your own model and trait methods.
If you have custom methods in your own model, you can call those functions directly from the Lunar model instance.

What this also means now the core model can forward call to your extended methods.
Scout in this case will also be made aware that shouldBeSearchable will return false.
Assuming we want to provide a new function to a product variant model.

```php
namespace App\Concerns;
<?php

trait SearchableTrait
namespace App\Models;

class ProductVariant extends \Lunar\Models\ProductVariant
{
/**
* Determine if the model should be searchable.
* @see \Laravel\Scout\Searchable::shouldBeSearchable()
*
* @return bool
*/
public function shouldBeSearchable()
public function someCustomMethod()
{
return false;
return 'Hello!';
}
}
```
#### Example 3 - Overriding cart address functionality (Cart Model)

```php
namespace App\Models;

use App\Concerns\HasAddresses;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;

/**
* Class Cart
*
* @property \Illuminate\Support\Collection $billingAddress
* @property \Illuminate\Support\Collection $shippingAddress
*
*/
class Cart extends \Lunar\Models\Cart
// In your service provider.
public function boot()
{
use HasAddresses;

/**
* {@inheritDoc}
*/
protected $casts = [
'completed_at' => 'datetime',
'meta' => AsArrayObject::class,
'shipping_data' => AsCollection::class,
];
\Lunar\Facades\ModelManifest::replace(
\Lunar\Models\Contracts\ProductVariant::class,
\App\Model\ProductVariant::class,
);
}
```
Note: You can override the casts in a model for example useful when adding new json fields.
In this example we are setting shipping_data cast to store as json. (You will of course need to create your migration)

The trait below demonstrates how to fully extend the cart model functionality.
Somewhere else in your app...

```php
namespace App\Concerns;

trait HasAddresses
{
/**
* Return the address relationships.
*
* @return \Illuminate\Support\Collection
*/
public function addresses(): Collection
{
return ! $this->isSameAddress()
? $this->billingAddress->merge($this->shippingAddress)
: $this->billingAddress;
}
\Lunar\Models\ProductVariant::someCustomMethod(); // Hello!
\App\Models\ProductVariant::someCustomMethod(); // Hello!
```

/**
* Return the shipping address relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function shippingAddress()
{
return $this->belongsTo(Address::class, 'shipping_address_id');
}
### Observers

/**
* Return the billing address relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function billingAddress()
{
return $this->belongsTo(Address::class, 'billing_address_id');
}
If you have observers in your app which call `observe` on the Lunar model, these will still work as intended when you replace any of the models, this means if you
want to add your own custom observers, you can just reference the Lunar model and everything will be forwarded to the appropriate class.

/**
* Compare the shipping and billing address to see if they are the same.
*
* @return bool
*/
protected function isSameAddress(): bool
{
return $this->billingAddress->first()->id === $this->shippingAddress->first()->id;
}
}
```php
\Lunar\Models\Product::observe(/** .. */);
```

## Dynamic Eloquent Relationships

Eloquent relationships can be dynamically specified in code, allowing you to add additional relationships to the Lunar Models.
If you don't need to completely override or extend the Lunar models using the techniques above, you are still free to resolve relationships dynamically as Laravel provides out the box.

e.g.

Expand All @@ -253,18 +145,3 @@ Order::resolveRelationUsing('ticket', function ($orderModel) {
```

See [https://laravel.com/docs/eloquent-relationships#dynamic-relationships](https://laravel.com/docs/eloquent-relationships#dynamic-relationships) for more information.


## Macroable

All Lunar models have been made macroable. This is a Laravel technique to allow a developer to dynamically add methods to an existing class. This is ideal for adding helpful functions for your application.

Here is an example...

```php
use Lunar\Models\Product;

Product::macro('isDraft', function () {
return $this->status === 'draft';
});
```
Loading

0 comments on commit 60c11ff

Please sign in to comment.