Skip to content

Commit e664582

Browse files
authored
Merge pull request #190 from WordPress/transformers_registration
Lay down foundation for utilizing TransformersRegistry class for transformers registration
2 parents 58e893e + 63aaa79 commit e664582

File tree

8 files changed

+237
-40
lines changed

8 files changed

+237
-40
lines changed

src/plugin/class-engine.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@
44

55
class Engine {
66

7-
private string $storage_post_type = 'liberated_data';
7+
public const string STORAGE_POST_TYPE = 'liberated_data';
88

99
public function __construct() {
10+
require 'enum-subject-type.php';
11+
12+
require 'class-transformersregistry.php';
1013
require 'class-post-type-ui.php';
1114
require 'class-transformer.php';
1215
require 'class-subjects-controller.php';
1316
require 'class-storage.php';
1417
require 'class-schema.php';
1518
require 'utils.php';
19+
require 'class-subject.php';
1620

1721
( function () {
18-
$transformer = new Transformer();
19-
20-
new Post_Type_UI( $this->storage_post_type, $transformer );
22+
new Transformer();
23+
new Post_Type_UI( self::STORAGE_POST_TYPE );
2124

2225
// REST API
23-
new Subjects_Controller( $this->storage_post_type );
26+
new Subjects_Controller( self::STORAGE_POST_TYPE );
2427

25-
new Storage( $this->storage_post_type );
28+
new Storage( self::STORAGE_POST_TYPE );
2629
} )();
2730
}
2831
}

src/plugin/class-post-type-ui.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
class Post_Type_UI {
66
private string $post_type;
7-
private Transformer $transformer;
87

9-
public function __construct( $custom_post_type, Transformer $transformer ) {
10-
$this->post_type = $custom_post_type;
11-
$this->transformer = $transformer;
8+
public function __construct( $custom_post_type ) {
9+
$this->post_type = $custom_post_type;
1210

1311
$this->remove_add_new_option( $this->post_type );
1412

@@ -100,7 +98,7 @@ function () {
10098
global $post;
10199

102100
$post_id = $post->ID;
103-
$transformed_post_id = $this->transformer->get_transformed_post_id( $post_id );
101+
$transformed_post_id = get_post_meta( $post_id, Transformer::META_KEY_LIBERATED_OUTPUT, true );
104102

105103
if ( $transformed_post_id ) {
106104
echo '<pre>PostID: ' . esc_html( $transformed_post_id ) . '</pre>';

src/plugin/class-subject.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace DotOrg\TryWordPress;
4+
5+
use WP_Post;
6+
7+
/**
8+
* Subject class offers an easy view of liberated data, abstracting away the implementation details
9+
*
10+
* WP specific data is private and only exposed via methods to limit leakage of implementation details
11+
* Raw data is available as public fields
12+
*/
13+
class Subject {
14+
15+
private int $id;
16+
private int $author_id;
17+
18+
public string $source_html;
19+
public string $source_url;
20+
public string $type;
21+
public string $title;
22+
public string $date;
23+
public string $content;
24+
25+
/**
26+
* Creates a new Subject instance from a WordPress post.
27+
*
28+
* @param int $post_id The WordPress post ID to create the subject from.
29+
* @return Subject|false The Subject instance or false if the post doesn't exist.
30+
*/
31+
public static function from_post( int $post_id ): Subject|false {
32+
$post = get_post( $post_id );
33+
34+
if ( ! $post instanceof WP_Post ) {
35+
return false;
36+
}
37+
38+
return new self( $post );
39+
}
40+
41+
/**
42+
* Private constructor to enforce using the factory method.
43+
*
44+
* @param WP_Post $post The WordPress post to create the subject from.
45+
*/
46+
private function __construct( WP_Post $post ) {
47+
$this->id = $post->ID;
48+
$this->author_id = $post->post_author;
49+
$this->source_html = $post->post_content_filtered;
50+
$this->source_url = $post->guid;
51+
52+
$this->type = get_post_meta( $post->ID, 'subject_type', true );
53+
$this->title = get_post_meta( $post->ID, 'raw_title', true );
54+
$this->date = get_post_meta( $post->ID, 'raw_date', true );
55+
$this->content = get_post_meta( $post->ID, 'raw_content', true );
56+
}
57+
58+
public function id(): int {
59+
return $this->id;
60+
}
61+
62+
public function author_id(): int {
63+
return $this->author_id;
64+
}
65+
66+
public function transformed_post_id(): int {
67+
return absint( get_post_meta( $this->id, Transformer::META_KEY_LIBERATED_OUTPUT, true ) );
68+
}
69+
}

src/plugin/class-subjects-controller.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ public function create_item( $request ): WP_REST_Response|WP_Error {
292292
$subject_type = $this->get_subject_type( $request );
293293
update_post_meta( $item['ID'], 'subject_type', $subject_type );
294294

295-
do_action( 'dl_data_saved', $item['ID'], 'create' );
295+
do_action( 'dl_data_saved', Subject::from_post( $item['ID'] ), 'create' );
296296

297297
return $this->prepare_item_for_response( $item, $request, $subject_type );
298298
}
@@ -316,7 +316,7 @@ public function update_item( $request ): WP_REST_Response|WP_Error {
316316
update_post_meta( $item['ID'], $key, $value );
317317
}
318318

319-
do_action( 'dl_data_saved', $item['ID'], 'update' );
319+
do_action( 'dl_data_saved', Subject::from_post( $item['ID'] ), 'update' );
320320

321321
return $this->prepare_item_for_response( $item, $request );
322322
}
@@ -382,7 +382,7 @@ public function prepare_item_for_response( $item, $request ): WP_REST_Response|W
382382
'authorId' => $item['post_author'] ?? '',
383383
'sourceUrl' => $item['guid'] ?? '',
384384
'sourceHtml' => $item['post_content_filtered'] ?? '',
385-
'transformedId' => absint( get_post_meta( $item['ID'], '_dl_transformed', true ) ),
385+
'transformedId' => absint( get_post_meta( $item['ID'], Transformer::META_KEY_LIBERATED_OUTPUT, true ) ),
386386
);
387387

388388
foreach ( array_keys( Schema::get()[ $subject_type ]['fields'] ) as $field_name ) {

src/plugin/class-transformer.php

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
use WP_Post;
66

77
class Transformer {
8-
private string $meta_key_for_transformed_post = '_dl_transformed';
8+
public const string META_KEY_LIBERATED_SOURCE = '_data_liberation_source';
9+
public const string META_KEY_LIBERATED_OUTPUT = '_data_liberation_output';
910

1011
public function __construct() {
1112
add_action( 'dl_data_saved', array( $this, 'transform' ), 10, 2 );
@@ -35,23 +36,15 @@ private function get_post_type_for_transformed_post( int|WP_Post $liberated_post
3536
return apply_filters( 'post_type_for_transformed_post', $post_type, $liberated_post );
3637
}
3738

38-
public function get_transformed_post_id( $liberated_post_id ): int|null {
39-
$value = get_post_meta( $liberated_post_id, $this->meta_key_for_transformed_post, true );
40-
if ( '' === $value ) {
41-
return null;
42-
}
43-
44-
return absint( $value );
45-
}
46-
47-
public function transform( int $liberated_post_id, string $verb ): bool {
39+
public function transform( Subject $subject, string $verb ): bool {
4840
if ( apply_filters( 'skip_native_transformation', false ) ) {
4941
return true;
5042
}
5143

52-
$liberated_post = get_post( $liberated_post_id );
44+
$liberated_post_id = $subject->id();
45+
$liberated_post = get_post( $liberated_post_id );
5346

54-
$transformed_post_id = get_post_meta( $liberated_post->ID, $this->meta_key_for_transformed_post, true );
47+
$transformed_post_id = get_post_meta( $liberated_post->ID, self::META_KEY_LIBERATED_OUTPUT, true );
5548

5649
$title = get_post_meta( $liberated_post->ID, 'parsed_title', true );
5750
if ( empty( $title ) ) {
@@ -86,7 +79,8 @@ public function transform( int $liberated_post_id, string $verb ): bool {
8679
return false;
8780
}
8881

89-
add_post_meta( $liberated_post->ID, $this->meta_key_for_transformed_post, $inserted_post_id );
82+
update_post_meta( $inserted_post_id, self::META_KEY_LIBERATED_SOURCE, $liberated_post->ID );
83+
update_post_meta( $liberated_post->ID, self::META_KEY_LIBERATED_OUTPUT, $inserted_post_id );
9084
return true;
9185
}
9286
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
namespace DotOrg\TryWordPress;
4+
5+
use Exception;
6+
use InvalidArgumentException;
7+
8+
class TransformersRegistry {
9+
private static string $user_choice_meta_key_prefix = '_data_liberation_chosen_handler_';
10+
private static array $handlers = array();
11+
12+
/**
13+
* Add a handler for a specific subject type
14+
*
15+
* @param SubjectType $type The subject type for which handler should be registered.
16+
* @param array $identifier Array containing unique slug and description.
17+
* @param callable $handler The handler function.
18+
* @return void
19+
* @throws InvalidArgumentException If handler is not callable.
20+
*/
21+
public static function add( SubjectType $type, array $identifier, callable $handler ): void {
22+
if ( ! is_callable( $handler ) ) {
23+
throw new InvalidArgumentException( 'Handler must be callable' );
24+
}
25+
26+
if ( ! isset( $identifier['slug'] ) ) {
27+
throw new InvalidArgumentException( 'Identifier slug must be defined' );
28+
}
29+
30+
if ( ! isset( self::$handlers[ $type->value ] ) ) {
31+
self::$handlers[ $type->value ] = array();
32+
}
33+
34+
self::$handlers[ $type->value ][ $identifier['slug'] ] = array(
35+
'slug' => $identifier['slug'],
36+
'description' => $identifier['desc'],
37+
'handler' => $handler,
38+
);
39+
}
40+
41+
/**
42+
* Check if handlers exist for a type
43+
*
44+
* @param SubjectType $type The subject type to check for.
45+
* @return bool True if handlers exist
46+
*/
47+
public static function has( SubjectType $type ): bool {
48+
return isset( self::$handlers[ $type->value ] ) && ! empty( self::$handlers[ $type->value ] );
49+
}
50+
51+
/**
52+
* Check if there is a "compete" i.e., multiple handlers for a type
53+
*
54+
* @param SubjectType $type The subject type to check for.
55+
* @return bool True if handlers exist
56+
*/
57+
public static function is_compete( SubjectType $type ): bool {
58+
return isset( self::$handlers[ $type->value ] ) && count( self::$handlers[ $type->value ] ) > 1;
59+
}
60+
61+
/**
62+
* Execute all handlers for a type
63+
*
64+
* @param SubjectType $type The subject type to handle.
65+
* @param Subject $subject Data to pass to handlers.
66+
* @return void
67+
* @throws Exception If no handler has been registered or user choice hasn't been set when multiples are registered.
68+
*/
69+
public static function handle( SubjectType $type, Subject $subject ): void {
70+
if ( ! self::has( $type ) ) {
71+
throw new Exception( sprintf( 'no handler registered for type: %s', esc_html( $type->value ) ) );
72+
}
73+
74+
if ( self::is_compete( $type ) ) {
75+
$choice = self::get_user_choice( $type );
76+
if ( ! empty( $choice ) ) {
77+
$chosen = self::$handlers[ $type->value ][ $choice ];
78+
} else {
79+
throw new Exception( 'handle() invoked without user choice on compete' );
80+
}
81+
} else {
82+
$chosen = current( self::$handlers[ $type->value ] );
83+
}
84+
85+
$transformed_post_id = $chosen['handler']( $subject );
86+
87+
if ( $transformed_post_id ) {
88+
update_post_meta( $subject->id(), Transformer::META_KEY_LIBERATED_OUTPUT, $transformed_post_id );
89+
update_post_meta( $transformed_post_id, Transformer::META_KEY_LIBERATED_SOURCE, $subject->id() );
90+
}
91+
}
92+
93+
/**
94+
* Remove all handlers for a type
95+
*
96+
* @param SubjectType $type The type to clear handlers for.
97+
* @return void
98+
*/
99+
public static function clear( SubjectType $type ): void {
100+
if ( isset( self::$handlers[ $type->value ] ) ) {
101+
unset( self::$handlers[ $type->value ] );
102+
}
103+
}
104+
105+
/**
106+
* Set user choice for what transformer to run for a subject type when multiples are registered
107+
*
108+
* @param SubjectType $type The subject type for which choice is to be saved.
109+
* @param string $transformer_slug Identifying slug of the chosen transformer.
110+
* @return void
111+
*/
112+
public static function set_user_choice( SubjectType $type, string $transformer_slug ): void {
113+
update_user_meta( get_current_user_id(), self::$user_choice_meta_key_prefix . $type->value, $transformer_slug );
114+
}
115+
116+
/**
117+
* Retrieves the user choice for what transformer to run for a subject type when multiples are registered
118+
*
119+
* @param SubjectType $type The subject type for which choice is to be retrieved.
120+
* @return string $transformer_slug Identifying slug of the chosen transformer.
121+
*/
122+
public static function get_user_choice( SubjectType $type ): string {
123+
return get_user_meta( get_current_user_id(), self::$user_choice_meta_key_prefix . $type->value, true );
124+
}
125+
}

src/plugin/enum-subject-type.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace DotOrg\TryWordPress;
4+
5+
enum SubjectType: string {
6+
case BLOGPOST = 'blog-post';
7+
case PAGE = 'page';
8+
case PRODUCT = 'product';
9+
10+
public function get_display_name(): string {
11+
return ucfirst( $this->value );
12+
}
13+
}

tests/plugin/test-transformer.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22

3+
use DotOrg\TryWordPress\Engine;
34
use DotOrg\TryWordPress\Transformer;
5+
use DotOrg\TryWordPress\Subject;
46
use PHPUnit\Framework\TestCase;
57

68
class Transformer_Test extends TestCase {
@@ -21,7 +23,7 @@ protected function setUp(): void {
2123
'post_status' => 'draft',
2224
'post_content_filtered' => '<div><p>Content 1</p><p>Content 2</p></div>',
2325
'guid' => 'https://example.com/x',
24-
'post_type' => 'liberated_data',
26+
'post_type' => Engine::STORAGE_POST_TYPE,
2527
)
2628
);
2729
update_post_meta( $this->post_id_in_db, 'subject_type', 'blog-post' );
@@ -32,11 +34,11 @@ protected function setUp(): void {
3234
protected function tearDown(): void {
3335
parent::tearDown();
3436

35-
$transformed_post_id = $this->transformer->get_transformed_post_id( $this->post_id_in_db );
37+
$transformed_post_id = get_post_meta( $this->post_id_in_db, Transformer::META_KEY_LIBERATED_OUTPUT, true );
3638
wp_delete_post( $transformed_post_id, true );
3739
wp_delete_post( $this->post_id_in_db, true );
3840

39-
delete_post_meta( 99, '_dl_transformed' );
41+
delete_post_meta( 99, Transformer::META_KEY_LIBERATED_OUTPUT );
4042
}
4143

4244
public function testGetPostTypeForTransformedPost() {
@@ -52,17 +54,10 @@ public function testGetPostTypeForTransformedPost() {
5254
$this->assertEquals( 'product', $result );
5355
}
5456

55-
public function testGetTransformedPost() {
56-
add_post_meta( 99, '_dl_transformed', 999 );
57-
58-
$this->assertEquals( 999, $this->transformer->get_transformed_post_id( 99 ) );
59-
$this->assertEquals( null, $this->transformer->get_transformed_post_id( 88 ) );
60-
}
61-
6257
public function testTransform(): void {
63-
$result = $this->transformer->transform( $this->post_id_in_db, 'whatever' ); // verb isn't currently used
58+
$result = $this->transformer->transform( Subject::from_post( $this->post_id_in_db ), 'whatever' ); // verb isn't currently used
6459

65-
$transformed_post_id = absint( get_post_meta( $this->post_id_in_db, '_dl_transformed', true ) );
60+
$transformed_post_id = absint( get_post_meta( $this->post_id_in_db, Transformer::META_KEY_LIBERATED_OUTPUT, true ) );
6661

6762
$this->assertEquals( $this->post_id_in_db + 1, $transformed_post_id );
6863
$this->assertTrue( $result );

0 commit comments

Comments
 (0)