diff --git a/php-wp-unit.xml b/php-wp-unit.xml index d9c07e2..71fd3e2 100644 --- a/php-wp-unit.xml +++ b/php-wp-unit.xml @@ -1,13 +1,12 @@ - + convertNoticesToExceptions="false" + convertWarningsToExceptions="false" + beStrictAboutTestsThatDoNotTestAnything="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> diff --git a/phpunit.xml b/phpunit.xml index 837590c..169ea16 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,12 +1,12 @@ + bootstrap="tests/bootstrap.php" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + beStrictAboutTestsThatDoNotTestAnything="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> ./src diff --git a/src/Abstracts/Connection.php b/src/Abstracts/Connection.php index a381ad4..0f9e6a5 100644 --- a/src/Abstracts/Connection.php +++ b/src/Abstracts/Connection.php @@ -7,7 +7,7 @@ class Connection implements IArrayConvertable { public ?int $id = 0; - protected ?string $title; + public ?string $title; public string $relation = ''; public int $from; public int $to; diff --git a/src/Abstracts/IQuery.php b/src/Abstracts/IQuery.php deleted file mode 100644 index a61114e..0000000 --- a/src/Abstracts/IQuery.php +++ /dev/null @@ -1,14 +0,0 @@ -set('id', $request->get_param('connectionID')); + try { + $queryConnection = new Query\Connection(); + $queryConnection->id = $request->get_param('connectionID'); - if ('PUT' === $request->get_method()) { - $queryConnection->meta->setIsUpdate(false); - } + $found = $this->getClient()->getRelation($request->get_param('relation'))->findConnections($queryConnection); - $queryConnection->meta->fromArray((array) $request->get_param('meta')); + if ($found->isEmpty()) { + return rest_ensure_response($this->getError(new ConnectionNotFound())); + } - if ('PATCH' === $request->get_method()) { - $queryConnection->meta->map( - function ($item) { - /** @var Query\Connection $item */ - $item->setIsUpdate(false); - } - ); - } + $connection = $found->first(); - try { - $result = $this->getClient()->getRelation($request->get_param('relation'))->updateConnectionMeta($queryConnection); + if ('PUT' === $request->get_method()) { + $connection->meta->clear(); + } + + if ('PATCH' === $request->get_method()) { + $filtered_meta = $connection->meta->filter(function (Meta $meta) use ($request) { + return ! in_array($meta->getKey(), array_column($request->get_param('meta'), 'key')); + }); + + $connection->meta->clear(); + $connection->meta->fromArray($filtered_meta->toArray()); + } + + $connection->meta->fromArray((array) $request->get_param('meta')); + + $connection->update(); } catch (Exception $e) { return rest_ensure_response($this->getError($e)); } - return [ 'updated' => $result ]; + return rest_ensure_response([ 'updated' => $connection ]); } public function restDeleteConnectionMeta(WP_REST_Request $request) diff --git a/src/Connection.php b/src/Connection.php index 2170a7c..ae15e51 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -2,6 +2,8 @@ namespace iTRON\wpConnections; +use iTRON\wpConnections\Exceptions\ConnectionWrongData; + class Connection extends Abstracts\Connection { use ClientInterface; @@ -19,10 +21,22 @@ public function __clone() } /** - * Saves instance to DB + * Saves instance to DB. + * Cannot be used to create new instance, only to update existing. + * This method overrides fields in a storage, not appends, and deletes all meta fields in a storage before saving. + * + * @throws ConnectionWrongData */ - public function save() + public function update(): void { + if (empty($this->id)) { + throw new ConnectionWrongData('Cannot update uninitialized connection', 304); + } + + $this->getClient()->getStorage()->updateConnection($this); + + $this->getClient()->getStorage()->removeConnectionMeta($this->id, new Query\MetaCollection()); + $this->getClient()->getStorage()->addConnectionMeta($this->id, $this->meta); } /** @@ -39,8 +53,12 @@ protected function loadFromQuery(Query\Connection $connectionQuery): Connection $this->relation = $connectionQuery->get('relation'); $this->from = $connectionQuery->get('from'); $this->to = $connectionQuery->get('to'); - $this->meta = clone $connectionQuery->get('meta'); + $this->meta = new MetaCollection(); + $this->meta->fromArray($connectionQuery->get('meta')->toArray()); $this->order = $connectionQuery->get('order'); + if ($connectionQuery->get('client')) { + $this->setClient($connectionQuery->get('client')); + } return $this; } diff --git a/src/ConnectionCollection.php b/src/ConnectionCollection.php index 185e9dd..37dffd9 100644 --- a/src/ConnectionCollection.php +++ b/src/ConnectionCollection.php @@ -46,16 +46,13 @@ private function fromArray(array $items, string $collectionType = ''): array if (! isset($item->to)) { continue; } - if (! isset($item->order)) { - continue; - } $connectionQuery = new Query\Connection(); $connectionQuery ->set('id', $item->ID) ->set('from', $item->from) ->set('to', $item->to) - ->set('order', $item->order) + ->set('order', $item->order ?? 0) ->set('title', $item->title ?? '') ->set('client', $item->client ?? null) ->set('relation', $item->relation ?? null); diff --git a/src/IQueryTrait.php b/src/IQueryTrait.php deleted file mode 100644 index 4c9dcec..0000000 --- a/src/IQueryTrait.php +++ /dev/null @@ -1,18 +0,0 @@ -isUpdate; - } - - public function setIsUpdate(bool $isUpdate) - { - $this->isUpdate = $isUpdate; - } -} diff --git a/src/Query/Connection.php b/src/Query/Connection.php index baa6579..46a086a 100644 --- a/src/Query/Connection.php +++ b/src/Query/Connection.php @@ -2,14 +2,11 @@ namespace iTRON\wpConnections\Query; -use iTRON\wpConnections\Abstracts\IQuery; use iTRON\wpConnections\GSInterface; -use iTRON\wpConnections\IQueryTrait; -class Connection extends \iTRON\wpConnections\Abstracts\Connection implements IQuery +class Connection extends \iTRON\wpConnections\Abstracts\Connection { use GSInterface; - use IQueryTrait; public int $both = 0; diff --git a/src/Query/MetaCollection.php b/src/Query/MetaCollection.php index 5ea20c8..2cee2d0 100644 --- a/src/Query/MetaCollection.php +++ b/src/Query/MetaCollection.php @@ -2,13 +2,10 @@ namespace iTRON\wpConnections\Query; -use iTRON\wpConnections\Abstracts\IQuery; use iTRON\wpConnections\GSInterface; -use iTRON\wpConnections\IQueryTrait; -class MetaCollection extends \iTRON\wpConnections\MetaCollection implements IQuery +class MetaCollection extends \iTRON\wpConnections\MetaCollection { - use IQueryTrait; use GSInterface; public string $collectionType = Meta::class; diff --git a/src/Relation.php b/src/Relation.php index 0e2c59b..6485ff6 100644 --- a/src/Relation.php +++ b/src/Relation.php @@ -88,52 +88,20 @@ public function createConnection(Query\Connection $connectionQuery): Connection $this->getClient()->getStorage()->createConnection($connectionQuery); - return new Connection($connectionQuery); + $connection = new Connection($connectionQuery); + $connection->setClient($this->getClient()); + + do_action('wpConnections/relation/created', $connection); + + return $connection; } - /** - * @throws ConnectionWrongData - */ public function updateConnection(Query\Connection $connectionQuery): bool { $connectionQuery->set('relation', $this->name); return $this->getClient()->getStorage()->updateConnection($connectionQuery); } - /** - * @throws ConnectionWrongData - */ - public function updateConnectionMeta(Query\Connection $connectionQuery): bool - { - $connectionQuery->set('relation', $this->name); - $objectID = $connectionQuery->get('id'); - - /** @var Query\MetaCollection $metaQuery */ - $metaQuery = $connectionQuery->get('meta'); - - if (! $metaQuery->isUpdate()) { - // Remove all meta fields first if false === isUpdate. - $this->getClient()->getStorage()->removeConnectionMeta($objectID, new Query\MetaCollection()); - } else { - // Check the array items for false === $isUpdate fields in order to remove older values. - $toRemove = $metaQuery->where('isUpdate', false); - if (! $toRemove->isEmpty()) { - foreach ($toRemove->getIterator() as $meta) { - /** @var Meta $meta */ - $meta->setValue(null); - } - $this->getClient()->getStorage()->removeConnectionMeta($objectID, $toRemove); - } - } - - // Finally, insert new meta fields. - if (! $metaQuery->isEmpty()) { - $this->getClient()->getStorage()->addConnectionMeta($objectID, $metaQuery); - } - - return true; - } - /** * @throws ConnectionWrongData */ diff --git a/src/Settings.php b/src/Settings.php index 0ecc811..df8ec21 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -22,7 +22,7 @@ protected function setLogging() }; add_action('wpConnections/storage/findConnections/dbQuery', $f, 10, 2); - add_action('wpConnections/storage/removeConnectionMeta/dbQuery', $f, 10, 2); + add_action('wpConnections/storage/removeConnectionMeta/after', $f, 10, 5); add_action('wpConnections/storage/deletedSpecificConnections', $f, 10, 3); } } diff --git a/src/WPStorage.php b/src/WPStorage.php index 8d64168..6daafa7 100644 --- a/src/WPStorage.php +++ b/src/WPStorage.php @@ -4,7 +4,6 @@ use iTRON\wpConnections\Exceptions\ConnectionWrongData; use iTRON\wpConnections\Helpers\Database; -use iTRON\wpConnections\Query\MetaCollection; class WPStorage extends Abstracts\Storage { @@ -327,7 +326,11 @@ public function findConnections(Query\Connection $params): ConnectionCollection $data[ $connection->ID ] = $item; } - return new ConnectionCollection($data); + $collection = new ConnectionCollection($data); + + do_action('wpConnections/storage/findConnections/dbQuery/data', $query, $query_result, $data, $collection->toArray()); + + return $collection; } /** @@ -381,61 +384,36 @@ public function createConnection(Query\Connection $connectionQuery): int return $connection_id; } - public function updateConnection(Query\Connection $connectionQuery): bool + public function updateConnection(Abstracts\Connection $connection): bool { global $wpdb; - $objectID = $connectionQuery->get('id'); - if (! is_numeric($objectID)) { - throw new Exceptions\ConnectionWrongData("Object ID is undefined."); - } - - $where = ['ID' => $objectID]; - $update = []; - - if (! is_null($title = $connectionQuery->get('title'))) { - $update['title'] = $title; - } - - if (! empty($from = $connectionQuery->get('from'))) { - $update['from'] = $from; - } - - if (! empty($to = $connectionQuery->get('to'))) { - $update['to'] = $to; - } - - if (! is_null($order = $connectionQuery->get('order'))) { - $update['order'] = $order; - } - - if (! $connectionQuery->isUpdate()) { - $update = $connectionQuery->toArray(); - - unset($update['meta']); - unset($update['relation']); - } - - if (! empty($update)) { - $updated = $wpdb->update($wpdb->prefix . $this->get_connections_table(), $update, $where); - } + $where = ['ID' => $connection->id]; + $update = [ + 'from' => $connection->from, + 'to' => $connection->to, + 'order' => $connection->order, + 'relation' => $connection->relation, + 'title' => $connection->title, + ]; - return true; + return $wpdb->update($wpdb->prefix . $this->get_connections_table(), $update, $where); } /** * Only adds meta fields to the DB. * * @param int $objectID - * @param MetaCollection $metaQuery + * @param MetaCollection $metaCollection + * * @return void * @throws ConnectionWrongData */ - public function addConnectionMeta(int $objectID, Query\MetaCollection $metaQuery) + public function addConnectionMeta(int $objectID, MetaCollection $metaCollection): void { global $wpdb; - if ($metaQuery->isEmpty()) { + if ($metaCollection->isEmpty()) { throw new Exceptions\ConnectionWrongData("Meta object is empty."); } @@ -443,8 +421,10 @@ public function addConnectionMeta(int $objectID, Query\MetaCollection $metaQuery throw new Exceptions\ConnectionWrongData("Object ID is empty."); } + do_action('wpConnections/storage/addConnectionMeta/before', $this->getClient(), $objectID, $metaCollection); + $errors = []; - foreach ($metaQuery->getIterator() as $meta) { + foreach ($metaCollection->getIterator() as $meta) { /** @var Query\Meta $meta */ $data = [ 'connection_id' => $objectID, @@ -452,14 +432,14 @@ public function addConnectionMeta(int $objectID, Query\MetaCollection $metaQuery 'meta_value' => $meta->getValue(), ]; - do_action('logger', $data); - $result = $wpdb->insert($wpdb->prefix . $this->get_meta_table(), $data); if (false === $result) { $errors [] = $wpdb->last_error; } } + do_action('wpConnections/storage/addConnectionMeta/after', $this->getClient(), $objectID, $metaCollection, $errors); + if ($errors) { $errors = implode('; ', $errors); throw new Exceptions\ConnectionWrongData("Database refused inserting new connection meta data with the words: [{$errors}]"); @@ -467,6 +447,10 @@ public function addConnectionMeta(int $objectID, Query\MetaCollection $metaQuery } /** + * Deletes meta fields from the DB. + * Provide Query\MetaCollection with meta fields to remove. + * Put empty Query\MetaCollection to remove all meta fields. + * * @throws ConnectionWrongData */ public function removeConnectionMeta(int $objectID, Query\MetaCollection $metaQuery) @@ -501,13 +485,12 @@ public function removeConnectionMeta(int $objectID, Query\MetaCollection $metaQu } $query = $from . implode(' ', $where); - $rowsAffected = $wpdb->query($query); - do_action('wpConnections/storage/removeConnectionMeta/dbQuery', $query, $rowsAffected); + do_action('wpConnections/storage/removeConnectionMeta/before', $this->getClient(), $objectID, $metaQuery, $query); - if (false === $rowsAffected) { - throw new Exceptions\ConnectionWrongData("Database refused removing new connection meta data with the words: [{$wpdb->last_error}]"); - } + $rowsAffected = $wpdb->query($query); + + do_action('wpConnections/storage/removeConnectionMeta/after', $this->getClient(), $objectID, $metaQuery, $query, $rowsAffected); return $rowsAffected; } diff --git a/tests/iTRON/wpConnections/WP/WPUnitTest.php b/tests/iTRON/wpConnections/WP/WPUnitTest.php index fa1e194..e7b2b24 100644 --- a/tests/iTRON/wpConnections/WP/WPUnitTest.php +++ b/tests/iTRON/wpConnections/WP/WPUnitTest.php @@ -3,10 +3,18 @@ namespace iTRON\wpConnections\Tests\iTRON\wpConnections\WP; use iTRON\wpConnections\Client; +use iTRON\wpConnections\ClientRestApi; +use iTRON\wpConnections\ConnectionCollection; +use iTRON\wpConnections\Exceptions\ConnectionWrongData; +use iTRON\wpConnections\Meta; use iTRON\wpConnections\Query\Connection; use iTRON\wpConnections\Query\Relation; use PHPUnit\Framework\TestCase; +use Ramsey\Collection\Exception\OutOfBoundsException; + +use function PHPUnit\Framework\assertEquals; + class WPUnitTest extends TestCase { protected Client $client; @@ -71,5 +79,208 @@ public function testCreateConnection() self::assertEquals( $connection_query->id, $connection->id ); self::assertEquals( $connection_query->from, $connection->from ); self::assertEquals( $connection_query->to, $connection->to ); + + return $connection; + } + + public function testFindConnections() { + // Create new page. + $page_id = wp_insert_post( [ + 'post_title' => 'Test Find Connections', + 'post_content' => 'Page content', + 'post_status' => 'publish', + 'post_type' => 'page', + ] ); + + // Create new post. + $post_id = wp_insert_post( [ + 'post_title' => 'Test Find Connections', + 'post_content' => 'Post content', + 'post_status' => 'publish', + 'post_type' => 'post', + ] ); + + // Create new connection. + $connection_query = new Connection( + $page_id, + $post_id + ); + $connection_query->title = 'Test Find Connections'; + + $connection = $this->client->getRelation( RELATION_NAME )->createConnection( $connection_query ); + + // Find the connection by ID. + $find_query = new Connection(); + $find_query->id = $connection->id; + + $found_connections = $this->client->getRelation( RELATION_NAME )->findConnections( $find_query ); + + self::assertEquals( 1, $found_connections->count() ); + self::assertEquals( $connection->id, $found_connections->first()->id ); + } + + /** + * @depends testCreateConnection + */ + public function testUpdateConnection( $connection ) + { + $title = 'New test title'; + $order = 10; + + // Create new page. + $page_id = wp_insert_post( [ + 'post_title' => 'Page 2', + 'post_content' => 'Page 2 content', + 'post_status' => 'publish', + 'post_type' => 'page', + ] ); + + // Create new post. + $post_id = wp_insert_post( [ + 'post_title' => 'Post 2', + 'post_content' => 'Post 2 content', + 'post_status' => 'publish', + 'post_type' => 'post', + ] ); + + // Find the connection. + $connection_query = new Connection( + $page_id, + $post_id + ); + $connection_query->title = 'Test Update Connection'; + + $connection = $this->client->getRelation( RELATION_NAME )->createConnection( $connection_query ); + + // Create new post. + $post_id = wp_insert_post( [ + 'post_title' => 'Post 3', + 'post_content' => 'Post 3 content', + 'post_status' => 'publish', + 'post_type' => 'post', + ] ); + + $connection->title = $title; + $connection->order = $order; + $connection->to = $post_id; + + $meta0 = new Meta( + 'key0', + 'value0' + ); + $connection->meta->add( $meta0 ); + + // Update the connection. + $connection->update(); + + // Find the connection again. + $connection = $this->client->getRelation( RELATION_NAME )->findConnections( $connection_query )->first(); + + self::assertEquals( $title, $connection->title ); + self::assertEquals( $order, $connection->order ); + self::assertEquals( $page_id, $connection->from ); + self::assertEquals( $post_id, $connection->to ); + self::assertEquals( 1, $connection->meta->count() ); + self::assertEquals( $meta0, $connection->meta->first() ); + + $meta1 = new Meta( + 'key1', + 'value1' + ); + $connection->meta->clear(); + $connection->meta->add( $meta1 ); + + // Update the connection. + $connection->update(); + + // Find the connection again. + $connection = $this->client->getRelation( RELATION_NAME )->findConnections( $connection_query )->first(); + + self::assertEquals( 1, $connection->meta->count() ); + self::assertEquals( $meta1, $connection->meta->first() ); + + // Test throwing exception when updating uninitialized connection. + $connection_query = new Connection( + $this->page_id, + $this->post_id + ); + + $connection = new \iTRON\wpConnections\Connection( $connection_query ); + + $this->expectException( ConnectionWrongData::class ); + $connection->update(); + } + + public function testRestUpdateMeta() { + // Create new page. + $page_id = wp_insert_post( [ + 'post_title' => 'Page for REST API tests', + 'post_content' => 'Page content', + 'post_status' => 'publish', + 'post_type' => 'page', + ] ); + + // Create new post. + $post_id = wp_insert_post( [ + 'post_title' => 'Post for REST API tests', + 'post_content' => 'Post content', + 'post_status' => 'publish', + 'post_type' => 'post', + ] ); + + // Find the connection. + $connection_query = new Connection( + $page_id, + $post_id + ); + + $connection = $this->client->getRelation( RELATION_NAME )->createConnection( $connection_query ); + $meta00 = new Meta( 'key0', 'value00' ); + $meta01 = new Meta( 'key0', 'value01' ); + $connection->meta->add( $meta00 ); + $connection->meta->add( $meta01 ); + $connection->update(); + + $request = new \WP_REST_Request( 'POST' ); + $meta1 = new Meta( 'key1', 'value1' ); + $request->set_param( 'connectionID', $connection->id ); + $request->set_param( 'meta', [$meta1->toArray()] ); + $request->set_param( 'relation', RELATION_NAME ); + + $restapi = new ClientRestApi( $this->client ); + $response = $restapi->restUpdateConnectionMeta( $request ); + + $connection->meta->add( $meta1 ); + self::assertEquals( 'WP_REST_Response', get_class( $response ) ); + self::assertEquals( $connection, $response->get_data()['updated'] ); + + $meta02 = new Meta( 'key0', 'value02' ); + $request = new \WP_REST_Request( 'PATCH' ); + $request->set_param( 'connectionID', $connection->id ); + $request->set_param( 'meta', [$meta02->toArray()] ); + $request->set_param( 'relation', RELATION_NAME ); + + $response = $restapi->restUpdateConnectionMeta( $request ); + $connection->meta->remove( $meta00 ); + $connection->meta->remove( $meta01 ); + $connection->meta->add( $meta02 ); + + self::assertEquals( 'WP_REST_Response', get_class( $response ) ); + self::assertEquals( $connection->meta->toArray(), $response->get_data()['updated']->meta->toArray() ); + + $meta3 = new Meta( 'key3', 'value3' ); + $meta4 = new Meta( 'key4', 'value4' ); + $request = new \WP_REST_Request( 'PUT' ); + $request->set_param( 'connectionID', $connection->id ); + $request->set_param( 'meta', [$meta3->toArray(), $meta4->toArray()] ); + $request->set_param( 'relation', RELATION_NAME ); + + $response = $restapi->restUpdateConnectionMeta( $request ); + $connection->meta->clear(); + $connection->meta->add( $meta3 ); + $connection->meta->add( $meta4 ); + + self::assertEquals( 'WP_REST_Response', get_class( $response ) ); + self::assertEquals( $connection->meta->toArray(), $response->get_data()['updated']->meta->toArray() ); } } diff --git a/tests/wp_bootstrap.php b/tests/wp_bootstrap.php index 1c066f5..3bf35d3 100644 --- a/tests/wp_bootstrap.php +++ b/tests/wp_bootstrap.php @@ -1,5 +1,11 @@