Skip to content

Commit

Permalink
Allow anonymous models to define events (#536)
Browse files Browse the repository at this point in the history
* Allow anonymous models to define events

* CHANGELOG

* Adding term support

* Linting fix
  • Loading branch information
srtfisher authored Apr 22, 2024
1 parent 40e6240 commit eb49bb5
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Prevent sending mail during the install `wp_install()` call in unit tests by
mocking the `$phpmailer` global earlier.
- Allow anonymous models to define events via `Model::created()` methods.

## v1.0.6 - 2024-04-19

Expand Down
19 changes: 19 additions & 0 deletions src/mantle/database/model/class-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ public static function find( $object ) {
*/
public static function for( string $post_type ): self {
$instance = new class() extends Post {
/**
* Constructor.
*/
public function __construct() {}

/**
* Post type for the model.
*/
Expand All @@ -192,9 +197,23 @@ public static function for( string $post_type ): self {
public static function get_object_name(): ?string {
return static::$for_object_name;
}

/**
* Boot the model if it has not been booted.
*
* Prevent booting the model unless the object name is set.
*/
public static function boot_if_not_booted(): void {
if ( empty( static::$for_object_name ) ) {
return;
}

parent::boot_if_not_booted();
}
};

$instance::$for_object_name = $post_type;
$instance::boot_if_not_booted();

return $instance;
}
Expand Down
28 changes: 22 additions & 6 deletions src/mantle/database/model/class-term.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ class Term extends Model implements Core_Object, Model_Meta, Updatable {
'term_id',
];

/**
* Object type for the model when registering the taxonomy.
*
* Used to associate a taxonomy with another object (post type).
*
* @var string[]
*/
protected static $object_types = [];

/**
* Constructor.
*
Expand Down Expand Up @@ -107,19 +116,26 @@ public static function find( $object ) {
public static function for( string $taxonomy ): self {
$instance = new class() extends Term {
/**
* Object name.
* Constructor.
*/
public static string $for_object_name = '';
public function __construct() {}

/**
* Retrieve the object name.
* Boot the model if it has not been booted.
*
* Prevent booting the model unless the object name is set.
*/
public static function get_object_name(): ?string {
return static::$for_object_name;
public static function boot_if_not_booted(): void {
if ( empty( static::$object_name ) ) {
return;
}

parent::boot_if_not_booted();
}
};

$instance::$for_object_name = $taxonomy;
$instance::$object_name = $taxonomy;
$instance::boot_if_not_booted();

return $instance;
}
Expand Down
1 change: 1 addition & 0 deletions src/mantle/database/model/events/trait-post-events.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function( $data, $postarr ) use ( $post_type ): void {

$updating = ! empty( $postarr['ID'] );
$model = static::find( $postarr['ID'] );

if ( ! $model ) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

/**
* Model Trait to allow a taxonomy to be registered for a model.
*
* @mixin \Mantle\Database\Model\Term
*/
trait Register_Taxonomy {
use Custom_Term_Link;
Expand Down Expand Up @@ -47,18 +49,20 @@ public static function register_object(): void {
* @throws Model_Exception Thrown on invalid class name being passed to object types.
*/
protected static function get_taxonomy_object_types(): array {
$object_types = (array) ( static::$object_types ?? [] ); // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.StaticOutsideClass
if ( empty( static::$object_types ) || ! is_array( static::$object_types ) ) {
return [];
}

foreach ( $object_types as &$object_type ) {
foreach ( static::$object_types as $key => $object_type ) {
// Detect a class name being used.
if ( false !== strpos( (string) $object_type, '\\' ) ) {
if ( false !== strpos( (string) $object_type, '\\' ) && class_exists( $object_type ) ) {
// Ensure the class name uses the Registrable Contract.
if ( ! in_array( Registrable_Contract::class, class_implements( $object_type ), true ) ) {
throw new Model_Exception( 'Unknown object type class provided: ' . $object_type );
}

// Convert the object type to the object's registration name.
$object_type = $object_type::get_registration_name();
static::$object_types[ $key ] = $object_type::get_registration_name();
}
}

Expand Down
40 changes: 40 additions & 0 deletions tests/Database/Model/PostModelEventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public function setUp(): void {
Testable_Model_Event::boot_post_events();
}

public function tearDown(): void {
parent::tearDown();

unregister_post_type( 'test-post-type' );
}

public function test_closure_event() {
$_SERVER['__test_creating_event_fired'] = false;
$_SERVER['__test_created_event_fired'] = false;
Expand Down Expand Up @@ -108,6 +114,40 @@ public function test_non_model_event() {
$this->assertTrue( $_SERVER['__test_non_model_created_event_fired'] < $_SERVER['__test_non_model_updated_event_fired'] );
$this->assertTrue( $_SERVER['__test_non_model_updated_event_fired'] < $_SERVER['__test_non_model_deleted_event_fired'] );
}

public function test_model_event_via_for() {
$_SERVER['__test_model_event_via_for_created_event_fired'] = false;
$_SERVER['__test_model_event_via_for_updated_event_fired'] = false;
$_SERVER['__test_model_event_via_for_deleted_event_fired'] = false;

register_post_type( 'test-post-type', [ 'public' => true ] );

$model = Post::for( 'test-post-type' );

$model->created( fn () => $_SERVER['__test_model_event_via_for_created_event_fired'] = microtime( true ) );
$model->updated( fn () => $_SERVER['__test_model_event_via_for_updated_event_fired'] = microtime( true ) );
$model->deleted( fn () => $_SERVER['__test_model_event_via_for_deleted_event_fired'] = microtime( true ) );

$post_id = \wp_insert_post(
[
'post_type' => 'test-post-type',
'post_title' => 'Inserted By Hand',
]
);

wp_update_post(
[
'ID' => $post_id,
'post_title' => 'Updated Title',
]
);

wp_delete_post( $post_id, true );

$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_created_event_fired'] );
$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_updated_event_fired'] );
$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_deleted_event_fired'] );
}
}

class Testable_Model_Event extends Post {
Expand Down
37 changes: 37 additions & 0 deletions tests/Database/Model/TermModelEventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public function setUp(): void {
Testable_Term_Model_Event::boot_term_events();
}

public function tearDown(): void {
parent::tearDown();

unregister_taxonomy( 'test-taxonomy' );
}

public function test_closure_event() {
$_SERVER['__test_creating_event_fired'] = false;
$_SERVER['__test_created_event_fired'] = false;
Expand Down Expand Up @@ -94,6 +100,37 @@ public function test_non_model_event() {
$this->assertTrue( $_SERVER['__test_non_model_created_event_fired'] < $_SERVER['__test_non_model_updated_event_fired'] );
$this->assertTrue( $_SERVER['__test_non_model_updated_event_fired'] < $_SERVER['__test_non_model_deleted_event_fired'] );
}

public function test_model_event_via_for() {
$_SERVER['__test_model_event_via_for_created_event_fired'] = false;
$_SERVER['__test_model_event_via_for_updated_event_fired'] = false;
$_SERVER['__test_model_event_via_for_deleted_event_fired'] = false;

register_taxonomy( 'test-taxonomy', 'post' );

$model = Term::for( 'test-taxonomy' );

$model->created( fn () => $_SERVER['__test_model_event_via_for_created_event_fired'] = microtime( true ) );
$model->updated( fn () => $_SERVER['__test_model_event_via_for_updated_event_fired'] = microtime( true ) );
$model->deleted( fn () => $_SERVER['__test_model_event_via_for_deleted_event_fired'] = microtime( true ) );

$insert = \wp_insert_term( 'Inserted Term', 'test-taxonomy' );
$term_id = $insert['term_id'];

\wp_update_term(
$term_id,
'test-taxonomy',
[
'name' => 'Updated Title',
]
);

wp_delete_term( $term_id, 'test-taxonomy' );

$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_created_event_fired'] );
$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_updated_event_fired'] );
$this->assertNotEmpty( $_SERVER['__test_model_event_via_for_deleted_event_fired'] );
}
}

class Testable_Term_Model_Event extends Term {
Expand Down
1 change: 1 addition & 0 deletions tests/Database/Model/registration/RegisterTaxonomyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ protected function setUp(): void {

protected function tearDown(): void {
parent::tearDown();
unregister_taxonomy( 'test-taxonomy' );
m::close();
}

Expand Down
2 changes: 2 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// Enable debugging flag for local development on the testing framework.
// define( 'MANTLE_TESTING_DEBUG', true );

(new \NunoMaduro\Collision\Provider)->register();

\Mantle\Testing\manager()
->maybe_rsync_plugin()
->with_vip_mu_plugins()
Expand Down

0 comments on commit eb49bb5

Please sign in to comment.