Skip to content

Commit

Permalink
Add relation database property (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariosimao authored Jan 10, 2023
1 parent 71ddb61 commit 706942c
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support to nullable page properties (#149)
- Properties: `Date`, `Email`, `Number`, `PhoneNumber`, `Select`, `Url`.
- New method `isEmpty()` on those properties.
- Support `Relation` database property (#150)

## [1.1.0]

Expand Down
1 change: 1 addition & 0 deletions src/Databases/Properties/PropertyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static function fromArray(array $array): PropertyInterface
PropertyType::Number => Number::fromArray($array),
PropertyType::People => People::fromArray($array),
PropertyType::PhoneNumber => PhoneNumber::fromArray($array),
PropertyType::Relation => Relation::fromArray($array),
PropertyType::RichText => RichTextProperty::fromArray($array),
PropertyType::Select => Select::fromArray($array),
PropertyType::Status => Status::fromArray($array),
Expand Down
139 changes: 139 additions & 0 deletions src/Databases/Properties/Relation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

namespace Notion\Databases\Properties;

use Notion\Exceptions\RelationException;

/**
* @psalm-type RelationJson = array{
* id: string,
* name: string,
* type: "relation",
* relation: array{
* database_id: string,
* type: string,
* single_property?: array<empty, empty>,
* dual_property?: array{
* synced_property_name: string,
* synced_property_id: string
* }
* }
* }
*
* @psalm-immutable
*/
class Relation implements PropertyInterface
{
private function __construct(
private readonly PropertyMetadata $metadata,
public readonly string $databaseId,
public readonly RelationType $type,
public readonly string|null $syncedPropertyName,
public readonly string|null $syncedPropertyId,
) {
if ($type === RelationType::DualProperty && $syncedPropertyName === null) {
throw RelationException::emptySyncedPropertyName();
}

if ($type === RelationType::DualProperty && $syncedPropertyId === null) {
throw RelationException::emptySyncedPropertyId();
}
}

public static function createUnidirectional(string $propertyName, string $databaseId): self
{
$metadata = PropertyMetadata::create("", $propertyName, PropertyType::Relation);
$type = RelationType::SingleProperty;

return new self($metadata, $databaseId, $type, null, null);
}

public static function createBidirectional(
string $propertyName,
string $databaseId,
string $syncedPropertyName,
string $syncedPropertyId,
): self {
$metadata = PropertyMetadata::create("", $propertyName, PropertyType::Relation);
$type = RelationType::DualProperty;

return new self($metadata, $databaseId, $type, $syncedPropertyName, $syncedPropertyId);
}

public function changeToUnidirectional(): self
{
$newType = RelationType::SingleProperty;

return new self($this->metadata(), $this->databaseId, $newType, null, null);
}

public function changeToBidirectional(string $syncedPropertyName, string $syncedPropertyId): self
{
$newType = RelationType::DualProperty;

return new self(
$this->metadata(),
$this->databaseId,
$newType,
$syncedPropertyName,
$syncedPropertyId
);
}

public function metadata(): PropertyMetadata
{
return $this->metadata;
}

public static function fromArray(array $array): self
{
/** @psalm-var RelationJson $array */
$metadata = PropertyMetadata::fromArray($array);

$databaseId = $array["relation"]["database_id"];
$type = RelationType::from($array["relation"]["type"]);

$syncedPropertyName = null;
$syncedPropertyId = null;
if ($type === RelationType::DualProperty) {
$syncedPropertyName = $array["relation"]["dual_property"]["synced_property_name"] ?? null;
$syncedPropertyId = $array["relation"]["dual_property"]["synced_property_id"] ?? null;
}

return new self($metadata, $databaseId, $type, $syncedPropertyName, $syncedPropertyId);
}

public function toArray(): array
{
$array = $this->metadata->toArray();
$relation = [
"database_id" => $this->databaseId,
"type" => $this->type->value,
];

if ($this->isUniderectional()) {
$relation["single_property"] = new \stdClass();
}

if ($this->isBiderectional()) {
$relation["dual_property"] = [
"synced_property_name" => $this->syncedPropertyName,
"synced_property_id" => $this->syncedPropertyId,
];
}

$array["relation"] = $relation;

return $array;
}

public function isUniderectional(): bool
{
return $this->type === RelationType::SingleProperty;
}

public function isBiderectional(): bool
{
return $this->type === RelationType::DualProperty;
}
}
9 changes: 9 additions & 0 deletions src/Databases/Properties/RelationType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Notion\Databases\Properties;

enum RelationType: string
{
case SingleProperty = "single_property";
case DualProperty = "dual_property";
}
16 changes: 16 additions & 0 deletions src/Exceptions/RelationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Notion\Exceptions;

class RelationException extends NotionException
{
public static function emptySyncedPropertyName(): self
{
return new self("Bidirectional relations must provide 'synced property name'.");
}

public static function emptySyncedPropertyId(): self
{
return new self("Bidirectional relations must provide 'synced property ID'.");
}
}
95 changes: 95 additions & 0 deletions tests/Unit/Databases/Properties/RelationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Notion\Test\Unit\Databases\Properties;

use Notion\Databases\Properties\PropertyFactory;
use Notion\Databases\Properties\Relation;
use PHPUnit\Framework\TestCase;

class RelationTest extends TestCase
{
public function test_create_unidirectional(): void
{
$databaseId = "04eecdf8-f2d9-43a0-abbc-476182192c8f";
$relation = Relation::createUnidirectional("My relation", $databaseId);

$this->assertSame("My relation", $relation->metadata()->name);
$this->assertSame($databaseId, $relation->databaseId);
$this->assertTrue($relation->isUniderectional());
}

public function test_create_bidirectional(): void
{
$databaseId = "04eecdf8-f2d9-43a0-abbc-476182192c8f";
$syncedPropertyName = "Prop name";
$syncedPropertyId = "12ac";

$relation = Relation::createBidirectional(
"My relation",
$databaseId,
$syncedPropertyName,
$syncedPropertyId,
);

$this->assertSame("My relation", $relation->metadata()->name);
$this->assertSame($databaseId, $relation->databaseId);
$this->assertSame($syncedPropertyName, $relation->syncedPropertyName);
$this->assertSame($syncedPropertyId, $relation->syncedPropertyId);
$this->assertTrue($relation->isBiderectional());
}

public function test_change_to_unidirectional(): void
{
$databaseId = "04eecdf8-f2d9-43a0-abbc-476182192c8f";
$syncedPropertyName = "Prop name";
$syncedPropertyId = "12ac";

$relation = Relation::createBidirectional(
"My relation",
$databaseId,
$syncedPropertyName,
$syncedPropertyId,
);

$relation = $relation->changeToUnidirectional();

$this->assertTrue($relation->isUniderectional());
$this->assertNull($relation->syncedPropertyId);
$this->assertNull($relation->syncedPropertyName);
}

public function test_change_to_bidirectional(): void
{
$databaseId = "04eecdf8-f2d9-43a0-abbc-476182192c8f";
$relation = Relation::createUnidirectional("My relation", $databaseId);

$syncedPropertyName = "Prop name";
$syncedPropertyId = "12ac";


$relation = $relation->changeToBidirectional($syncedPropertyName, $syncedPropertyId);

$this->assertTrue($relation->isBiderectional());
$this->assertSame($syncedPropertyName, $relation->syncedPropertyName);
$this->assertSame($syncedPropertyId, $relation->syncedPropertyId);
}

public function test_array_conversion(): void
{
$array = [
"id" => "abc",
"name" => "dummy",
"type" => "relation",
"relation" => [
"database_id" => "84660ad0-9cb9-45d0-aae0-91e2c2526e12",
"type" => "single_property",
"single_property" => new \stdClass(),
],
];
$relation = Relation::fromArray($array);
$fromFactory = PropertyFactory::fromArray($array);

$this->assertEquals($array, $relation->toArray());
$this->assertEquals($array, $fromFactory->toArray());
}
}

0 comments on commit 706942c

Please sign in to comment.