Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: RDF Object Mapper PoC #3447

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use oat\tao\model\import\ServiceProvider\ImportServiceProvider;
use oat\tao\model\metadata\ServiceProvider\MetadataServiceProvider;
use oat\tao\model\Observer\ServiceProvider\ObserverServiceProvider;
use oat\tao\model\RdfObjectMapper\ServiceProvider\RdfObjectMapperServiceProvider;
use oat\tao\model\resources\ResourcesServiceProvider;
use oat\tao\model\featureFlag\FeatureFlagServiceProvider;
use oat\tao\helpers\form\ServiceProvider\FormServiceProvider;
Expand Down Expand Up @@ -297,5 +298,6 @@
AccessControlServiceProvider::class,
MetadataServiceProvider::class,
ObserverServiceProvider::class,
RdfObjectMapperServiceProvider::class,
],
];
103 changes: 103 additions & 0 deletions models/classes/RdfObjectMapper/Annotation/RdfAttributeMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?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;

use core_kernel_classes_Property;
use core_kernel_classes_Resource;
use Psr\Log\LoggerInterface;
use ReflectionProperty;

//#[\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);
}*/

// You may see keeping hydrate() here in the annotation class as a good
// thing (as in "higher cohesion", keeping the value initialization in the
// annotation implementation) or bad thing (as in "higher coupling",
// preventing having more than one behaviour (implementation) for a given
// annotation).
//
// Maybe we'll want to allow overriding the behaviour for a given annotation
// somehow in the future (for example, from extensions)).
//
public function hydrate(
LoggerInterface $logger,
ReflectionProperty $property,
core_kernel_classes_Resource $src,
object $targetObject
): void
{
$logger->debug(__CLASS__ . " maps a value to the property");

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

if(count($values) == 0) {
$logger->warning(__CLASS__ . " no value to map");
return;
}

if(count($values) > 1) {
$logger->warning(__CLASS__ . "too many values to map");
return;
}

if(count($values) == 1) {
$value = current($values);
$logger->info(
sprintf("%s Mapping value %s into %s",
__CLASS__,
$value,
$property->getName()
)
);

$property->setAccessible(true);
$property->setValue($targetObject, $value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?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;

use core_kernel_classes_Resource;
use LogicException;
use Psr\Log\LoggerInterface;
use ReflectionProperty;

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

// the commented out constructors are in case we use PHP 8 annotations
//public function __construct(int $attributeType)
/*public function __construct($data)
{
//$this->attributeType = $attributeType;

//echo "Called RdfResourceAttributeMapping ctor with ".var_export($data,true);
}*/

public function hydrate(
LoggerInterface $logger,
ReflectionProperty $property,
core_kernel_classes_Resource $src,
object $targetObject
): void
{
$logger->debug(
__CLASS__ .
' maps a (direct) value from the resource class to the property'
);

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
);
}

$logger->info(
sprintf("%s Mapping value %s into %s",
__CLASS__,
$value,
$property->getName()
)
);

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,28 @@
<?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;
Comment on lines +25 to +27
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having only LITERAL (for copying values as-is) and RESOURCE (for URIs pointing to other resources) may suffice.

The former values types here were matching exactly the fields used for the PoC (basically attributes currently found in core_kernel_classes_Resource; as well as having a URI type to hold resource URIs (for example, URIs used as primary identifiers for resources).

This may be combined with PHP 7.4+ property type hints as well to validate things like conversions to integer etc.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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\Contract;

use core_kernel_classes_Resource;

interface RdfObjectMapperInterface
{
public function mapResource(
core_kernel_classes_Resource $resource,
string $targetClass
): object;
}
119 changes: 119 additions & 0 deletions models/classes/RdfObjectMapper/Example/UserSettingsMappedType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?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\Example;

use oat\generis\model\GenerisRdf;
use oat\tao\model\user\UserSettingsInterface;

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


/**
* Example object type with RDF mapping annotations.
*
* This would be "userland code" for the mapper (i.e. objects using annotations
* are to be located in other models/namespaces (tao/models/classes/user etc)
*/
class UserSettingsMappedType implements UserSettingsInterface
{
//#[RdfResourceAttributeMapping(RdfResourceAttributeMapping::URI)]
/** @RdfResourceAttributeMapping(type = RdfResourceAttributeType::URI) */
private /*string*/ $userUri;

//#[RdfResourceAttributeMapping(RdfResourceAttributeMapping::LABEL)]
/** @RdfResourceAttributeMapping(type = RdfResourceAttributeType::LABEL) */
private /*string*/ $userLabel;

//#[RdfResourceAttributeMapping(RdfResourceAttributeMapping::COMMENT)]
/** @RdfResourceAttributeMapping(type = RdfResourceAttributeType::COMMENT) */
private /*string*/ $userComment;

/*#[RdfAttributeMapping(
GenerisRdf::PROPERTY_USER_UILG,
'resource',

// field returned for non-literal properties.
// Can be
// - 'alias' (string)
// - 'range' (core_kernel_classes_ContainerCollection, calls $property->getRange())
// - 'resource' (complete resource instance)
// - 'uri' (resource URI as string)
'uri'
)]*/

/** @RdfAttributeMapping(
* propertyUri = GenerisRdf::PROPERTY_USER_UILG,
* attributeType = "resource",
* mappedField = "uri")
*/
private /*?string*/ $uiLanguageCode;

/**
* @RdfAttributeMapping(
* propertyUri = GenerisRdf::PROPERTY_USER_DEFLG,
* attributeType = "resource",
* mappedField = "uri")
*/
private /*?string*/ $dataLanguage;

/**
* property returned as a core_kernel_classes_Literal instance,
* will be converted to string
* @todo Use RdfResourceAttributeType (or a new class) constants for
* attributeType
* @RdfAttributeMapping(
* GenerisRdf::PROPERTY_USER_TIMEZONE,
* attributeType = "literal")
*/
private /*string*/ $timezone;

public function getUri(): string
{
return $this->userUri;
}

public function getLabel(): string
{
return $this->userLabel;
}

public function getComment(): string
{
return $this->userComment;
}

public function getUILanguageCode(): ?string
{
return $this->uiLanguageCode;
}

public function getDataLanguageCode(): ?string
{
return $this->dataLanguage;
}

public function getTimezone(): string
{
return $this->timezone;
}
}
Loading