Skip to content

Commit

Permalink
feat: PoC for object mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
hectoras committed Mar 10, 2022
1 parent f867f01 commit 4f782b5
Show file tree
Hide file tree
Showing 7 changed files with 473 additions and 0 deletions.
65 changes: 65 additions & 0 deletions models/classes/rdfObjectMapper/Annotation/RdfAttributeMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace oat\tao\model\RdfObjectMapper\Annotation;

//#[\Attribute(\Attribute::TARGET_PROPERTY)]
/**
*@Annotation
*/
class RdfAttributeMapping
{
public /*string*/ $propertyUri;
public /*string*/ $attributeType = 'resource';
public /*string*/ $mappedField = null;

// the commented out constructors are in case we use PHP 8 annotations
/*public function __construct(
string $propertyUri,
string $attributeType = 'resource',
string $mappedField = ''
)
{
$this->propertyUri = $propertyUri;
$this->attributeType = $attributeType;
$this->mappedField = $mappedField;
}*/

/*public function __construct() //($data, $b, $c)
{
//$this->attributeType = $attributeType;
//echo "Called RdfAttributeMapping ctor with ".var_export($data,true);
}*/

public function hydrate(
\ReflectionProperty $property,
\core_kernel_classes_Resource $src,
object &$targetObject
): void
{
echo __CLASS__ . " should map a value to the property<br/>\n";

$values = $src->getPropertyValues(
new \core_kernel_classes_Property($this->propertyUri)
);

if(count($values) == 0) {
echo "No value to map";
return;
}

if(count($values) > 1) {
echo "too many values to map";
return;
}

if(count($values) == 1) {
$value = current($values);
echo "Mapping value {$value} into {$property->getName()}<br/>";

$property->setAccessible(true);
$property->setValue($targetObject, $value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace oat\tao\model\RdfObjectMapper\Annotation;

//#[\Attribute(\Attribute::TARGET_PROPERTY)]
/**
*@Annotation
*/
class RdfResourceAttributeMapping
{
public /*int*/ $type = 0;

//public function __construct(int $attributeType)
/*public function __construct($data)
{
//$this->attributeType = $attributeType;
//echo "Called RdfResourceAttributeMapping ctor with ".var_export($data,true);
}*/

public function hydrate(
\ReflectionProperty $property,
\core_kernel_classes_Resource $src,
object &$targetObject
): void
{
echo __CLASS__ .
" should map a (direct) value from".
" the resource class to the property<br/>\n";

$value = null;
switch ($this->type)
{
case RdfResourceAttributeType::LABEL:
$value = $src->getLabel();
break;
case RdfResourceAttributeType::COMMENT:
$value = $src->getComment();
break;
case RdfResourceAttributeType::URI:
$value = $src->getUri();
break;
default:
throw new \LogicException(
"Unknown ".__CLASS__."::type value: ".
$this->type
);
}

echo "Mapping value {$value} into {$property->getName()}<br/>";

if (version_compare(PHP_VERSION, '8.1.0', '<')) {
// Not needed starting PHP 8.1 (it has become a no-op since then)
$property->setAccessible(true);
}

$property->setValue($targetObject, $value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2022 (original work) Open Assessment Technologies SA.
*/

namespace oat\tao\model\RdfObjectMapper\Annotation;

class RdfResourceAttributeType
{
public const URI = 1;
public const LABEL = 2;
public const COMMENT = 3;
}
97 changes: 97 additions & 0 deletions models/classes/rdfObjectMapper/Hydrator/ResourceHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2022 (original work) Open Assessment Technologies SA.
*/

namespace oat\tao\model\RdfObjectMapper\TargetTypes;


require_once __DIR__ . '/../Annotation/RdfAttributeMapping.php';
require_once __DIR__ . '/../Annotation/RdfResourceAttributeMapping.php';
require_once __DIR__ . '/../Annotation/RdfResourceAttributeType.php';

use oat\tao\model\RdfObjectMapper\Annotation\RdfAttributeMapping;
use oat\tao\model\RdfObjectMapper\Annotation\RdfResourceAttributeMapping;
use oat\tao\model\RdfObjectMapper\Annotation\RdfResourceAttributeType;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;

use core_kernel_classes_Resource;
use ReflectionClass;
use ReflectionException;
use Exception;

class ResourceHydrator
{
/** @var AnnotationReader */
private $reader;

public function __construct()
{
$this->reader = new AnnotationReader();

// Too bad this pollutes the global annotation reader state
AnnotationRegistry::loadAnnotationClass(RdfResourceAttributeMapping::class);
AnnotationRegistry::loadAnnotationClass(RdfAttributeMapping::class);
}

public function hydrateInstance(
ReflectionClass $reflector,
core_kernel_classes_Resource $src,
object &$targetObject
)
{
foreach($reflector->getProperties() as $property) {
echo $property->getName()."<br/>\n";

$propertyAnnotations = $this->reader->getPropertyAnnotations(
$property
);

echo "Property {$property->getName()} annotations<br/>\n";

foreach ($propertyAnnotations as $annotation)
{
// @todo We may delegate the initialization to the annotation
// class itself (i.e. pass a ref to the attribute and the
// value)?
if($annotation instanceof RdfResourceAttributeMapping)
{
echo "-> RdfResourceAttributeMapping<br/>\n";
$annotation->hydrate($property, $src, $targetObject);
continue;
}

if($annotation instanceof RdfAttributeMapping)
{
echo "-> RdfAttributeMapping<br/>\n";
$annotation->hydrate($property, $src, $targetObject);
continue;
}

throw new Exception(
"Unknown class property type: " . get_class($annotation)
);
}

echo "<br/>\n", "<br/>\n", "<br/>\n";
}

}
}
94 changes: 94 additions & 0 deletions models/classes/rdfObjectMapper/RdfObjectMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2021 (original work) Open Assessment Technologies SA.
*/

namespace oat\tao\model\RdfObjectMapper;

use oat\tao\model\RdfObjectMapper\TargetTypes\RdfResourceAttributeMapping;
use oat\tao\model\RdfObjectMapper\TargetTypes\ResourceHydrator;
use RdfAttributeMapping;
use core_kernel_classes_Resource;
use ReflectionClass;
use ReflectionException;

require_once __DIR__ . '/Annotation/RdfAttributeMapping.php';
require_once __DIR__ . '/Annotation/RdfResourceAttributeMapping.php';
require_once __DIR__ . '/Hydrator/ResourceHydrator.php';

// taoDockerize uses PHP 7.2 and Generis is supporting "php": "^7.1":
// we cannot use native annotations.
//
// However, Generis explicitly depends on doctrine/annotations ~1.6.0,
// we may reimplement this using Doctrine annotations instead.
//
// Maybe this should be in Generis instead?
// @todo The object mapper may be an interface with right now a single
// implementation that uses PHPDoc annotations. Maybe the object mapper
// can just use delegate the mapping to a "child" mapper with something
// like a chain of responsibility, so we can have a mapper based on
// Doctrine annotations while also having the possibility to implement a
// different one using PHP native annotations in the future.
class RdfObjectMapper
{
/** @var ResourceHydrator */
private $hydrator;

public function __construct()
{
$this->hydrator = new ResourceHydrator();
}

public function mapResource(
core_kernel_classes_Resource $resource,
string $targetClass
): object
{
$reflector = $this->reflect($targetClass);
$instance = $reflector->newInstanceWithoutConstructor();

$this->hydrator->hydrateInstance($reflector, $resource, $instance);

echo "instance hydrated:<br/>";
echo "<pre>".var_export($instance, true)."</pre>";

$this->callConstructorIfPresent($reflector, $instance);

return $instance;
}

/**
* @throws ReflectionException
*/
private function reflect(string $targetClass): ReflectionClass
{
return new ReflectionClass($targetClass);
}

private function callConstructorIfPresent(
ReflectionClass $reflector,
object $instance
)
{
if($reflector->getConstructor() != null)
{
$closure = $reflector->getConstructor()->getClosure();
$closure->call($instance);
}
}
}
Loading

0 comments on commit 4f782b5

Please sign in to comment.