Skip to content
Merged
15 changes: 9 additions & 6 deletions src/plugin/class-engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@

class Engine {

private string $storage_post_type = 'liberated_data';
public const string STORAGE_POST_TYPE = 'liberated_data';

public function __construct() {
require 'enum-subject-type.php';

require 'class-transformersregistry.php';
require 'class-post-type-ui.php';
require 'class-transformer.php';
require 'class-subjects-controller.php';
require 'class-storage.php';
require 'class-schema.php';
require 'utils.php';
require 'class-subject.php';

( function () {
$transformer = new Transformer();

new Post_Type_UI( $this->storage_post_type, $transformer );
new Transformer();
new Post_Type_UI( self::STORAGE_POST_TYPE );

// REST API
new Subjects_Controller( $this->storage_post_type );
new Subjects_Controller( self::STORAGE_POST_TYPE );

new Storage( $this->storage_post_type );
new Storage( self::STORAGE_POST_TYPE );
} )();
}
}
8 changes: 3 additions & 5 deletions src/plugin/class-post-type-ui.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

class Post_Type_UI {
private string $post_type;
private Transformer $transformer;

public function __construct( $custom_post_type, Transformer $transformer ) {
$this->post_type = $custom_post_type;
$this->transformer = $transformer;
public function __construct( $custom_post_type ) {
$this->post_type = $custom_post_type;

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

Expand Down Expand Up @@ -100,7 +98,7 @@ function () {
global $post;

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

if ( $transformed_post_id ) {
echo '<pre>PostID: ' . esc_html( $transformed_post_id ) . '</pre>';
Expand Down
69 changes: 69 additions & 0 deletions src/plugin/class-subject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace DotOrg\TryWordPress;

use WP_Post;

/**
* Subject class offers an easy view of liberated data, abstracting away the implementation details
*
* WP specific data is private and only exposed via methods to limit leakage of implementation details
* Raw data is available as public fields
*/
class Subject {

private int $id;
private int $author_id;

public string $source_html;
public string $source_url;
public string $type;
public string $title;
public string $date;
public string $content;

/**
* Creates a new Subject instance from a WordPress post.
*
* @param int $post_id The WordPress post ID to create the subject from.
* @return Subject|false The Subject instance or false if the post doesn't exist.
*/
public static function from_post( int $post_id ): Subject|false {
$post = get_post( $post_id );

if ( ! $post instanceof WP_Post ) {
return false;
}

return new self( $post );
}

/**
* Private constructor to enforce using the factory method.
*
* @param WP_Post $post The WordPress post to create the subject from.
*/
private function __construct( WP_Post $post ) {
$this->id = $post->ID;
$this->author_id = $post->post_author;
$this->source_html = $post->post_content_filtered;
$this->source_url = $post->guid;

$this->type = get_post_meta( $post->ID, 'subject_type', true );
$this->title = get_post_meta( $post->ID, 'raw_title', true );
$this->date = get_post_meta( $post->ID, 'raw_date', true );
$this->content = get_post_meta( $post->ID, 'raw_content', true );
}

public function id(): int {
return $this->id;
}

public function author_id(): int {
return $this->author_id;
}

public function transformed_post_id(): int {
return absint( get_post_meta( $this->id, Transformer::META_KEY_LIBERATED_OUTPUT, true ) );
}
}
6 changes: 3 additions & 3 deletions src/plugin/class-subjects-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public function create_item( $request ): WP_REST_Response|WP_Error {
$subject_type = $this->get_subject_type( $request );
update_post_meta( $item['ID'], 'subject_type', $subject_type );

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

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

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

return $this->prepare_item_for_response( $item, $request );
}
Expand Down Expand Up @@ -382,7 +382,7 @@ public function prepare_item_for_response( $item, $request ): WP_REST_Response|W
'authorId' => $item['post_author'] ?? '',
'sourceUrl' => $item['guid'] ?? '',
'sourceHtml' => $item['post_content_filtered'] ?? '',
'transformedId' => absint( get_post_meta( $item['ID'], '_dl_transformed', true ) ),
'transformedId' => absint( get_post_meta( $item['ID'], Transformer::META_KEY_LIBERATED_OUTPUT, true ) ),
);

foreach ( array_keys( Schema::get()[ $subject_type ]['fields'] ) as $field_name ) {
Expand Down
22 changes: 8 additions & 14 deletions src/plugin/class-transformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use WP_Post;

class Transformer {
private string $meta_key_for_transformed_post = '_dl_transformed';
public const string META_KEY_LIBERATED_SOURCE = '_data_liberation_source';
public const string META_KEY_LIBERATED_OUTPUT = '_data_liberation_output';

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

public function get_transformed_post_id( $liberated_post_id ): int|null {
$value = get_post_meta( $liberated_post_id, $this->meta_key_for_transformed_post, true );
if ( '' === $value ) {
return null;
}

return absint( $value );
}

public function transform( int $liberated_post_id, string $verb ): bool {
public function transform( Subject $subject, string $verb ): bool {
if ( apply_filters( 'skip_native_transformation', false ) ) {
return true;
}

$liberated_post = get_post( $liberated_post_id );
$liberated_post_id = $subject->id();
$liberated_post = get_post( $liberated_post_id );

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

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

add_post_meta( $liberated_post->ID, $this->meta_key_for_transformed_post, $inserted_post_id );
update_post_meta( $inserted_post_id, self::META_KEY_LIBERATED_SOURCE, $liberated_post->ID );
update_post_meta( $liberated_post->ID, self::META_KEY_LIBERATED_OUTPUT, $inserted_post_id );
return true;
}
}
125 changes: 125 additions & 0 deletions src/plugin/class-transformersregistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

namespace DotOrg\TryWordPress;

use Exception;
use InvalidArgumentException;

class TransformersRegistry {
private static string $user_choice_meta_key_prefix = '_data_liberation_chosen_handler_';
private static array $handlers = array();

/**
* Add a handler for a specific subject type
*
* @param SubjectType $type The subject type for which handler should be registered.
* @param array $identifier Array containing unique slug and description.
* @param callable $handler The handler function.
* @return void
* @throws InvalidArgumentException If handler is not callable.
*/
public static function add( SubjectType $type, array $identifier, callable $handler ): void {
if ( ! is_callable( $handler ) ) {
throw new InvalidArgumentException( 'Handler must be callable' );
}

if ( ! isset( $identifier['slug'] ) ) {
throw new InvalidArgumentException( 'Identifier slug must be defined' );
}

if ( ! isset( self::$handlers[ $type->value ] ) ) {
self::$handlers[ $type->value ] = array();
}

self::$handlers[ $type->value ][ $identifier['slug'] ] = array(
'slug' => $identifier['slug'],
'description' => $identifier['desc'],
'handler' => $handler,
);
}

/**
* Check if handlers exist for a type
*
* @param SubjectType $type The subject type to check for.
* @return bool True if handlers exist
*/
public static function has( SubjectType $type ): bool {
return isset( self::$handlers[ $type->value ] ) && ! empty( self::$handlers[ $type->value ] );
}

/**
* Check if there is a "compete" i.e., multiple handlers for a type
*
* @param SubjectType $type The subject type to check for.
* @return bool True if handlers exist
*/
public static function is_compete( SubjectType $type ): bool {
return isset( self::$handlers[ $type->value ] ) && count( self::$handlers[ $type->value ] ) > 1;
}

/**
* Execute all handlers for a type
*
* @param SubjectType $type The subject type to handle.
* @param Subject $subject Data to pass to handlers.
* @return void
* @throws Exception If no handler has been registered or user choice hasn't been set when multiples are registered.
*/
public static function handle( SubjectType $type, Subject $subject ): void {
if ( ! self::has( $type ) ) {
throw new Exception( sprintf( 'no handler registered for type: %s', esc_html( $type->value ) ) );
}

if ( self::is_compete( $type ) ) {
$choice = self::get_user_choice( $type );
if ( ! empty( $choice ) ) {
$chosen = self::$handlers[ $type->value ][ $choice ];
} else {
throw new Exception( 'handle() invoked without user choice on compete' );
}
} else {
$chosen = current( self::$handlers[ $type->value ] );
}

$transformed_post_id = $chosen['handler']( $subject );

if ( $transformed_post_id ) {
update_post_meta( $subject->id(), Transformer::META_KEY_LIBERATED_OUTPUT, $transformed_post_id );
update_post_meta( $transformed_post_id, Transformer::META_KEY_LIBERATED_SOURCE, $subject->id() );
}
}

/**
* Remove all handlers for a type
*
* @param SubjectType $type The type to clear handlers for.
* @return void
*/
public static function clear( SubjectType $type ): void {
if ( isset( self::$handlers[ $type->value ] ) ) {
unset( self::$handlers[ $type->value ] );
}
}

/**
* Set user choice for what transformer to run for a subject type when multiples are registered
*
* @param SubjectType $type The subject type for which choice is to be saved.
* @param string $transformer_slug Identifying slug of the chosen transformer.
* @return void
*/
public static function set_user_choice( SubjectType $type, string $transformer_slug ): void {
update_user_meta( get_current_user_id(), self::$user_choice_meta_key_prefix . $type->value, $transformer_slug );
}

/**
* Retrieves the user choice for what transformer to run for a subject type when multiples are registered
*
* @param SubjectType $type The subject type for which choice is to be retrieved.
* @return string $transformer_slug Identifying slug of the chosen transformer.
*/
public static function get_user_choice( SubjectType $type ): string {
return get_user_meta( get_current_user_id(), self::$user_choice_meta_key_prefix . $type->value, true );
}
}
13 changes: 13 additions & 0 deletions src/plugin/enum-subject-type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace DotOrg\TryWordPress;

enum SubjectType: string {
case BLOGPOST = 'blog-post';
case PAGE = 'page';
case PRODUCT = 'product';

public function get_display_name(): string {
return ucfirst( $this->value );
}
}
19 changes: 7 additions & 12 deletions tests/plugin/test-transformer.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

use DotOrg\TryWordPress\Engine;
use DotOrg\TryWordPress\Transformer;
use DotOrg\TryWordPress\Subject;
use PHPUnit\Framework\TestCase;

class Transformer_Test extends TestCase {
Expand All @@ -21,7 +23,7 @@ protected function setUp(): void {
'post_status' => 'draft',
'post_content_filtered' => '<div><p>Content 1</p><p>Content 2</p></div>',
'guid' => 'https://example.com/x',
'post_type' => 'liberated_data',
'post_type' => Engine::STORAGE_POST_TYPE,
)
);
update_post_meta( $this->post_id_in_db, 'subject_type', 'blog-post' );
Expand All @@ -32,11 +34,11 @@ protected function setUp(): void {
protected function tearDown(): void {
parent::tearDown();

$transformed_post_id = $this->transformer->get_transformed_post_id( $this->post_id_in_db );
$transformed_post_id = get_post_meta( $this->post_id_in_db, Transformer::META_KEY_LIBERATED_OUTPUT, true );
wp_delete_post( $transformed_post_id, true );
wp_delete_post( $this->post_id_in_db, true );

delete_post_meta( 99, '_dl_transformed' );
delete_post_meta( 99, Transformer::META_KEY_LIBERATED_OUTPUT );
}

public function testGetPostTypeForTransformedPost() {
Expand All @@ -52,17 +54,10 @@ public function testGetPostTypeForTransformedPost() {
$this->assertEquals( 'product', $result );
}

public function testGetTransformedPost() {
add_post_meta( 99, '_dl_transformed', 999 );

$this->assertEquals( 999, $this->transformer->get_transformed_post_id( 99 ) );
$this->assertEquals( null, $this->transformer->get_transformed_post_id( 88 ) );
}

public function testTransform(): void {
$result = $this->transformer->transform( $this->post_id_in_db, 'whatever' ); // verb isn't currently used
$result = $this->transformer->transform( Subject::from_post( $this->post_id_in_db ), 'whatever' ); // verb isn't currently used

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

$this->assertEquals( $this->post_id_in_db + 1, $transformed_post_id );
$this->assertTrue( $result );
Expand Down
Loading