aboutsummaryrefslogtreecommitdiffhomepage
path: root/Digitigrade/Model/FetchableModel.php
diff options
context:
space:
mode:
authorwinter2024-12-09 21:31:54 +0000
committerwinter2024-12-09 21:31:54 +0000
commit4c7bde1400820f36caf8b2a5374007384c3018f3 (patch)
tree83ed26ff368117b8e392f7bc28736482077fc2f4 /Digitigrade/Model/FetchableModel.php
parent50edbd3907c26a2d4d616421de9bb51a4d083c1b (diff)
rename to Digitigrade / PawPub
:3
Diffstat (limited to 'Digitigrade/Model/FetchableModel.php')
-rw-r--r--Digitigrade/Model/FetchableModel.php78
1 files changed, 78 insertions, 0 deletions
diff --git a/Digitigrade/Model/FetchableModel.php b/Digitigrade/Model/FetchableModel.php
new file mode 100644
index 0000000..30b68e7
--- /dev/null
+++ b/Digitigrade/Model/FetchableModel.php
@@ -0,0 +1,78 @@
+<?php
+namespace Digitigrade\Model;
+
+use Digitigrade\Model;
+use Digitigrade\RemoteFetchable;
+
+abstract class FetchableModel extends Model implements RemoteFetchable {
+ private static \JsonMapper $mapper;
+
+ public static function __initStatic() {
+ self::$mapper = new \JsonMapper();
+ // this is unfortunately necessary to parse DateTime(Immutable)s properly
+ self::$mapper->bStrictObjectTypeChecking = false;
+ }
+
+ private static function fetchFromRemote(string $uri, bool $autoSave): ?static {
+ // fetch the object
+ $data = @file_get_contents($uri, context: stream_context_create(['http' => [
+ 'header' => 'Accept: application/json'
+ ]]));
+ if ($data === false)
+ return null;
+ $data = json_decode($data);
+ if ($data === null)
+ return null;
+ // default everything nullable to null
+ $obj = new static();
+ $reflProps = (new \ReflectionClass(static::class))->getProperties();
+ foreach ($reflProps as $reflProp) {
+ assert($reflProp instanceof \ReflectionProperty); // for intellisense lmao
+ if ($reflProp->getType()->allowsNull()) {
+ $obj->{$reflProp->getName()} = null;
+ }
+ }
+ // rename 'self' to 'uri' because of reasons
+ if (property_exists($data, 'self')) {
+ $data->uri = $data->self;
+ unset($data->self);
+ }
+ // map all present fields
+ self::$mapper->map($data, $obj);
+ // (hopefully) recursively fetch any other fetchable models contained in this one
+ foreach ($reflProps as $reflProp) {
+ assert($reflProp instanceof \ReflectionProperty);
+ $reflType = $reflProp->getType();
+ $propName = $reflProp->getName();
+ if (!($reflType instanceof \ReflectionNamedType))
+ continue;
+ $type = $reflType->getName();
+ if (is_subclass_of($type, FetchableModel::class) && is_string($data->{$propName})) {
+ // try to avoid recursion
+ // TODO: make a better effort at avoiding recursion (e.g. when it's more than one step nested)
+ if ($data->{$propName} == $uri) {
+ $obj->{$propName} = $obj;
+ } else {
+ $obj->{$propName} = $type::findByUri($data->{$propName}, $autoSave);
+ }
+ }
+ }
+
+ if ($autoSave) {
+ $obj->save();
+ }
+
+ return $obj;
+ }
+
+ public static function findByUri(string $uri, bool $autoSave = true, bool $forceRefetch = false): ?static {
+ if (!$forceRefetch) {
+ $obj = static::findWhere('uri = ?', [$uri]);
+ if ($obj != null)
+ return $obj;
+ }
+ return static::fetchFromRemote($uri, $autoSave);
+ }
+}
+
+FetchableModel::__initStatic(); \ No newline at end of file