Skip to content

Commit

Permalink
Refactor plugin to use multiple files and a PHP autoloader
Browse files Browse the repository at this point in the history
  • Loading branch information
kellenmace committed Oct 14, 2020
1 parent f3dae19 commit eb5de34
Show file tree
Hide file tree
Showing 14 changed files with 809 additions and 131 deletions.
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
"name": "harness-software/wp-graphql-posts-to-posts",
"description": "WPGraphQL for Posts 2 Posts",
"type": "wordpress-plugin",
"license": "GPL-2.0",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "Kellen Mace",
"email": "kellen@harnessup.com"
}
],
"require": {}
"require": {},
"autoload": {
"psr-4": {
"WPGraphQLPostsToPosts\\": "src/"
}
}
}
131 changes: 131 additions & 0 deletions src/Connections/ConnectionsRegistrar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

namespace WPGraphQLPostsToPosts\Connections;

use P2P_Connection_Type;
use GraphQL\Type\Definition\ResolveInfo;
use WPGraphQL\AppContext;
use WPGraphQL\Data\DataSource;
use WPGraphQL\Data\Connection;
use WPGraphQL\Model\User;
use WPGraphQLPostsToPosts\Interfaces\Hookable;

class ConnectionsRegistrar implements Hookable {
/**
* Registered Posts2Posts connections.
*
* @var array
*/
private $p2p_connections = [];

/**
* Post types exposed in the GraphQL schema.
*
* @var array
*/
private $post_types = [];

public function register_hooks() {
add_action( 'p2p_registered_connection_type', [ $this, 'capture_p2p_connections' ], 10, 2 );
add_action( 'graphql_register_types', [ $this, 'set_post_types_property' ] );
add_action( 'graphql_register_types', [ $this, 'register_connections' ], 11 );
}

public function capture_p2p_connections( P2P_Connection_Type $ctype, array $args ) : void {
$this->p2p_connections[] = $args;
}

public function set_post_types_property() {
$this->post_types = get_post_types( [ 'show_in_graphql' => true ], 'objects' );
}

public function register_connections() {
$p2p_connections_to_map = array_filter( $this->p2p_connections, [ $this, 'should_create_connection' ] );

foreach( $p2p_connections_to_map as $p2p_connection ) {
// Register from -> to connection.
$this->register_connection( [
'from_object_name' => $p2p_connection['from'],
'to_object_name' => $p2p_connection['to'],
'connection_name' => $p2p_connection['name'],
] );

// Register to -> from connection.
$this->register_connection( [
'from_object_name' => $p2p_connection['to'],
'to_object_name' => $p2p_connection['from'],
'connection_name' => $p2p_connection['name'],
] );
}
}

private function register_connection( array $args ) : void {
register_graphql_connection( [
'fromType' => $this->get_graphql_single_name( $args['from_object_name'] ),
'toType' => $this->get_graphql_single_name( $args['to_object_name'] ),
'fromFieldName' => graphql_format_field_name( $args['connection_name'] . 'Connection' ),
'resolve' => function( $source, array $request_args, AppContext $context, ResolveInfo $info ) use ( $args ) {
// We need to query for connected users.
if ( 'user' === $args['to_object_name'] ) {
$resolver = new Connection\UserConnectionResolver( $source, $request_args, $context, $info, $args['to_object_name'] );
// We need to query for connected posts.
} else {
$resolver = new Connection\PostObjectConnectionResolver( $source, $request_args, $context, $info, $args['to_object_name'] );
$resolver->setQueryArg( 'post_parent', null );
$resolver->setQueryArg( 'author', null );
}

$source_object_id = $source instanceof User ? $source->userId : $source->ID;
$resolver->setQueryArg( 'connected_items', $source_object_id );
$resolver->setQueryArg( 'connected_type', $args['connection_name'] );
$connection = $resolver->get_connection();

return $connection;
},
] );
}

private function should_create_connection( array $connection ) : bool {
return $this->should_connect_object( $connection['from'] )
&& $this->should_connect_object( $connection['to'] );
}

private function should_connect_object( string $object_name ) : bool {
return 'user' === $object_name || $this->is_post_type_in_schema( $object_name );
}

private function is_post_type_in_schema( string $post_type_name ) : bool {
$post_type_names = array_map( fn( $post_type ) => $post_type->name, $this->post_types );

return in_array( $post_type_name, $post_type_names, true );
}

private function get_graphql_single_name( string $object_name ) : string {
if ( 'user' === $object_name ) {
return 'User';
}

$post_object = $this->array_find( $this->post_types, fn( $post_type ) => $post_type->name === $object_name );

return $post_object->graphql_single_name;
}

/**
* Get the value of the first array element that satisfies the callback function.
* Similar to JavaScript's Array.prototype.find() method.
*
* @param array $array The array.
* @param callable $callback The callback function.
*
* @return mixed The value of the element, or null if not found.
*/
private function array_find( array $array, callable $callback ) {
foreach ( $array as $key => $value ) {
if ( $callback( $value, $key, $array ) ) {
return $value;
}
}

return null;
}
}
15 changes: 15 additions & 0 deletions src/Interfaces/Hookable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace WPGraphQLPostsToPosts\Interfaces;

/**
* Interface Hookable
*/
interface Hookable {
/**
* Register hooks with WordPress.
*
* @return void
*/
public function register_hooks();
}
37 changes: 37 additions & 0 deletions src/WPGraphQLPostsToPosts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace WPGraphQLPostsToPosts;

use WPGraphQLPostsToPosts\Interfaces\Hookable;

/**
* Main plugin class.
*/
final class WPGraphQLPostsToPosts {
/**
* Class instances.
*/
private $instances = [];

/**
* Main method for running the plugin.
*/
public function run() {
$this->create_instances();
$this->register_hooks();
}

private function create_instances() {
$this->instances['connections_registrar'] = new Connections\ConnectionsRegistrar();
}

private function register_hooks() {
foreach ( $this->get_hookable_instances() as $instance ) {
$instance->register_hooks();
}
}

private function get_hookable_instances() {
return array_filter( $this->instances, fn( $instance ) => $instance instanceof Hookable );
}
}
7 changes: 7 additions & 0 deletions vendor/autoload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitfc2ebba91741ccdd8d50eb1e2ef478ce::getLoader();
Loading

0 comments on commit eb5de34

Please sign in to comment.