Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gpanos committed Aug 23, 2021
0 parents commit 0c6ebfb
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/code-style.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: code style

on: [push]

jobs:
php-cs-fixer:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Run PHP CS Fixer
uses: docker://oskarstark/php-cs-fixer-ga:2.19.0
with:
args: --config=.php_cs.dist --allow-risky=yes

- name: Extract branch name
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
id: extract_branch

- name: Commit changes
uses: stefanzweifel/[email protected]
with:
commit_message: Fix styling
branch: ${{ steps.extract_branch.outputs.branch }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42 changes: 42 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: tests

on:
push:
pull_request:
schedule:
- cron: '0 0 * * *'

jobs:
tests:
runs-on: ubuntu-latest

strategy:
fail-fast: true
matrix:
php: [8.0]
laravel: [^8.0]

name: P${{ matrix.php }} - L${{ matrix.laravel }}

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Cache dependencies
uses: actions/cache@v1
with:
path: ~/.composer/cache/files
key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip
coverage: none

- name: Install dependencies
run: composer require "illuminate/contracts=${{ matrix.laravel }}" --prefer-dist --no-interaction

- name: Execute tests
run: vendor/bin/phpunit --verbose
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
composer.lock
/vendor
.phpunit.result.cache
.php_cs.cache
.DS_Store
41 changes: 41 additions & 0 deletions .php_cs.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

$finder = Symfony\Component\Finder\Finder::create()
->notPath('vendor')
->in(__DIR__)
->name('*.php');

return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'not_operator_with_successor_space' => true,
'no_extra_blank_lines' => [
'curly_brace_block',
'extra',
'parenthesis_brace_block',
'throw',
'use',
],
'no_unused_imports' => true,
'ordered_imports' => ['sortAlgorithm' => 'alpha'],
'ternary_operator_spaces' => true,
'single_blank_line_before_namespace' => true,

// PSR-12
'blank_line_after_opening_tag' => true,
'braces' => ['allow_single_line_closure' => true],
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'none'],
'function_typehint_space' => true,
'new_with_braces' => true,
'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'],
'no_empty_statement' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_whitespace_in_blank_line' => true,
'return_type_declaration' => ['space_before' => 'none'],
'single_trait_insert_per_statement' => true,
])
->setFinder($finder);
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# PHP 8 attribute to register Laravel model observers.

Instead of defining [observers](https://laravel.com/docs/8.x/eloquent#observers) inside service providers this package offers an alternative way to register model observers for your Laravel applications.

## Installation

Under development. For now make sure to configure the repository in your composer.json by running:

```bash
composer config repositories.laravel-observe-attribute vcs https://github.com/gpanos/laravel-observe-attribute
```

Then install the package by running:

```bash
composer require gpanos/laravel-observe-attribute
```

## Usage

To register an observer add the `Observe` attribute to your model and pass it your observer class.

```php
<?php

#[Observe(UserObserver::class)]
class User extends Authenticatable
{
...
}
```



54 changes: 54 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "gpanos/laravel-observe-attribute",
"license": "MIT",
"description": "Register model observers using php 8 attributes",
"keywords": [
"laravel",
"observers"
],
"authors": [
{
"name": "Dimitris Karapanos",
"email": "[email protected]"
}
],
"homepage": "https://github.com/gpanos/laravel-observe-attribute",
"require": {
"php": "^8.0",
"illuminate/support": "^8.0",
"illuminate/database": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
"mockery/mockery": "^1.3",
"orchestra/testbench": "^6.0",
"friendsofphp/php-cs-fixer": "^2.16"
},
"autoload": {
"psr-4": {
"Gpanos\\ObserveAttribute\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Gpanos\\ObserveAttribute\\Tests\\": "tests"
}
},
"extra": {
"laravel": {
"providers": [
"Gpanos\\ObserveAttribute\\ObserveAttributeServiceProvider"
]
}
},
"scripts": {
"format": [
"vendor/bin/php-cs-fixer fix"
]
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
25 changes: 25 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Package">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src/</directory>
</whitelist>
</filter>
<php>
<env name="DB_CONNECTION" value="testing"/>
</php>
</phpunit>
13 changes: 13 additions & 0 deletions src/Observe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Gpanos\ObserveAttribute;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class Observe
{
public function __construct(public object|array|string $observer)
{
}
}
22 changes: 22 additions & 0 deletions src/ObserveAttributeServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Gpanos\ObserveAttribute;

use Illuminate\Support\ServiceProvider;

class ObserveAttributeServiceProvider extends ServiceProvider
{
public function boot(): void
{
(new ObserverRegistrar())
->useRootNamespace(app()->getNamespace())
->registerDirectory($this->getDirectory());
}

protected function getDirectory(): string
{
$testClassDirectory = __DIR__ . '/../tests/Stubs';

return app()->runningUnitTests() ? __DIR__ . '/../tests/Stubs' : app_path('Models');
}
}
82 changes: 82 additions & 0 deletions src/ObserverRegistrar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Gpanos\ObserveAttribute;

use Illuminate\Support\Str;
use ReflectionClass;
use SplFileInfo;
use Symfony\Component\Finder\Finder;

class ObserverRegistrar
{
public $basePath;

public $rootNamespace;

public function __construct()
{
$this->basePath = app()->path();
}

public function useBasePath(string $basePath): self
{
$this->basePath = $basePath;

return $this;
}

public function useRootNamespace(string $rootNamespace): self
{
$this->rootNamespace = $rootNamespace;

return $this;
}

public function registerDirectory(string $directory): void
{
$files = (new Finder())->files()->name('*.php')->in($directory);

collect($files)->each(fn (SplFileInfo $file) => $this->registerFile($file));
}

public function registerFile(string | SplFileInfo $path): void
{
if (is_string($path)) {
$path = new SplFileInfo($path);
}

$fullyQualifiedClassName = $this->fullQualifiedClassNameFromFile($path);

$this->processAttributes($fullyQualifiedClassName);
}

protected function fullQualifiedClassNameFromFile(SplFileInfo $file): string
{
$class = trim(Str::replaceFirst($this->basePath, '', $file->getRealPath()), DIRECTORY_SEPARATOR);

$class = str_replace(
[DIRECTORY_SEPARATOR, 'App\\'],
['\\', app()->getNamespace()],
ucfirst(Str::replaceLast('.php', '', $class))
);

return $this->rootNamespace . $class;
}

protected function processAttributes(string $className): void
{
if (! class_exists($className)) {
return;
}

$class = new ReflectionClass($className);

$attributes = $class->getAttributes(Observe::class);

foreach ($attributes as $attribute) {
$observer = $attribute->newInstance()->observer;

$className::observe($observer);
}
}
}
27 changes: 27 additions & 0 deletions tests/ObserverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Gpanos\ObserveAttribute\Tests;

use Gpanos\ObserveAttribute\Tests\Stubs\EloquentTestObserverStub;
use Gpanos\ObserveAttribute\Tests\Stubs\Models\EloquentModelStub;
use Illuminate\Contracts\Events\Dispatcher;
use Mockery as m;

class ObserverTest extends TestCase
{
public function test_it_registers_the_observers()
{
EloquentModelStub::setEventDispatcher($events = m::mock(Dispatcher::class));

$events->shouldReceive('listen')->once()->with('eloquent.creating: ' . EloquentModelStub::class, EloquentTestObserverStub::class . '@creating');
$events->shouldReceive('listen')->once()->with('eloquent.saved: ' . EloquentModelStub::class, EloquentTestObserverStub::class . '@saved');
$events->shouldReceive('forget');
$events->shouldReceive('dispatch');

$this
->observerRegistrar
->registerDirectory($this->getTestPath('Stubs/Models'));

EloquentModelStub::flushEventListeners();
}
}
Loading

0 comments on commit 0c6ebfb

Please sign in to comment.