solid-data-workers/laravel-sparql

A SPARQL based Eloquent model and Query builder for Laravel

dev-master 2021-03-27 22:16 UTC

This package is not auto-updated.

Last update: 2024-03-20 11:51:49 UTC


README

An Eloquent model and Query builder with support for SPARQL, using the original Laravel API.

Heavily based on the original MIT licensed Illuminate Database package, Copyright (c) Taylor Otwell.

Installation

Installation using composer:

composer require solid-data-workers/laravel-sparql

The service provider will register a sparql database extension with the original database manager. There is no need to register additional facades or objects. When using sparql connections, Laravel will automatically provide you with the corresponding sparql objects.

For usage outside Laravel, check out the Capsule manager and add:

$capsule->getDatabaseManager()->extend('sparql', function($config, $name)
{
    $config['name'] = $name;

    return new SolidDataWorkers\SPARQL\Connection($config);
});

Configuration

Change your default database connection name in config/database.php:

'default' => env('DB_CONNECTION', 'sparql'),

And add a new sparql connection:

'sparql' => [
    'driver'     => 'sparql',

    /*
        The SPARQL endpoint
    */
    'host'       => env('DB_HOST', 'https://dbpedia.org/sparql'),

    /*
        Optional.
        Authentication credentials for the SPARQL endpoint.
        Useful to deal, for example, with a own writable Virtuoso server
    */
    'auth'       => [
        'type' => 'digest',
        'username' => 'your_username',
        'password' => 'your_password',
    ],

    /*
        It is optional, but highly reccomended, to define your RDF namespaces.
        If you don't, a set of default namespaces is used (the one from EasyRDF) but this widely slows down the initialization of the internal Introspector (see below) at least at the first execution.
        Many features about attributes access will not work properly for undefined namespaces.
    */
    'namespaces' => [
        'owl'    => 'http://www.w3.org/2002/07/owl#',
        'rdf'    => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
        'rdfs'   => 'http://www.w3.org/2000/01/rdf-schema#',
        'dbo'    => 'http://dbpedia.org/ontology/',
        'schema' => 'http://schema.org/',
        'foaf'   => 'http://xmlns.com/foaf/0.1/',
    ],

    /*
        Optional.
        If you have other local ontology files to be loaded into the Introspector (e.g. for the full DBPedia ontology), use "ontologies" in the configuration file
    */
    'ontologies' => [
        '/path/to/my/file.owl',
    ],

    /*
        You can also define the default graph name on which run queries
    */
    'graph'      => 'urn:my:named:graph',
],

Expressions

SolidDataWorkers\SPARQL\Query\Expression is an utility class to identify and properly wrap and handle strings of different type: literal strings, URNs, classes and more.

When passing a value to any function of the query builder, any plain string is converted to an Expression wrapping a plain string. To specify an exact type for the string, instanciate your own Expression such as:

// Doesn't wraps the value within quotes or other
$e = Expression::raw('dbr:Philadelphia');
// Explodes the class name and wraps it within angular brackets
$e = Expression::cls('foaf:Person');

Query Builder

The database driver plugs right into the original query builder. When using sparql connections, you will be able to build fluent queries to perform database operations. For your convenience, there is a rdftype alias for table as well as some additional SPARQL specific operators/operations.

$people = DB::rdftype('foaf:Person')->get();

$people = DB::rdftype('foaf:Person')->where('foaf:gender', 'male')->first();

If you did not change your default database connection, you will need to specify it when querying.

$people = DB::connection('sparql')->rdftype('foaf:Person')->get();

Read more about the query builder on http://laravel.com/docs/queries

Introspector

The Introspector is an internal component used to keep a reference graph used to guess relations, datatypes for objects attributes, and provide some basic and transparent reasoning feature.

For each involved namespace it fetches the related RDF definition from the full URL (which may require some time), and the loaded graph is cached using the native Cache from Laravel. When the list of namespaces changes in the configuration, the cached graph is automatically invalidated and regenerated.

If you want to manually invalidate the internal graph, just call

Cache::forget('sparql_introspector_graph');

Eloquent

This package includes a SPARQL enabled Eloquent class that you can use to define models for corresponding RDF classes.

By default, for each basic class (owl:Class, or those defined by the basic_classes configuration) found by the Introspector, a new PHP Model class is created, named after his shortened name, and then used by the Builder to instantiate the results fetched from the SPARQL endpoint, such as:

namespace SolidDataWorkers\SPARQL\Eloquent;
use SolidDataWorkers\SPARQL\Eloquent\Model;

class ModelFoafPerson extends Model {
    protected $table = "foaf:Person";
}

To retrieve the Model class associated to each RDF class, call:

$model = DB::getIntrospector()->getModel('dbo:Person');
$new_model = new $model();

You can create your own classes extending SolidDataWorkers\SPARQL\Eloquent\Model and specifying the mapped class through the $table attribute. Those classes will not be created by the Introspector. Anyway, remember that relations are already implicitly guessed by the Introspector and - on the contrary of vanilla Laravel's Eloquent - you don't need to define them manually in your Model.

use SolidDataWorkers\SPARQL\Eloquent\Model;

class Person extends Model {
    protected $table = "foaf:Person";
}

class Place extends Model {
    protected $table = "http://dbpedia.org/ontology/PopulatedPlace";
}

Everything else should work just like the original Eloquent model. Well: I'm working on it... Read more about the Eloquent on http://laravel.com/docs/eloquent

Examples

Basic Usage

Retrieving All Models

$people = Person::all();

Retrieving A Record By Primary Key

$person = Person::find('http://dbpedia.org/resource/Al_Pacino');

Handling Properties

All properties are always encapsulated into a Illuminate\Support\Collection even when there is only a single value, so their behaviour is consistent across different properties types.

$name = $person->offsetGet('http://xmlns.com/foaf/0.1/name');
$name = $person->offsetGet('foaf:name');
$name = $person->foaf_name;

The last example showcase a convenient shortcut in which namespace and property name are separated with an underscore instead of a colon. If a named attribute is not actually present into the Model instance, it tries anyway to get it dynamically from the SPARQL endpoint and returns NULL when nothing is found.

$person->offsetSet('http://xmlns.com/foaf/0.1/name', 'Al');
$person->offsetSet('foaf:name', 'Al');
$person->foaf_name = 'Al';

Same for attribute setting: multiple ways to name them and assign a value.

$place_name = $person->dbo_birthPlace;

Relations are automatically resolved by the Introspector, so accessing an attribute referencing another object it is automagically loaded into a Model and accessible through his own attributes. Please note that those relations are themselves properties, so are always returned into a Illuminate\Support\Collection.

Wheres

$places = Place::where('dbo:areaTotal', '>', 10000000000)->get();

And Statements

$places = Place::where('dbo:areaTotal', '>', 10000000000)->where('dbo:country', Expression::raw('dbr:Italy'))->get();

Or Statements (TODO)

$places = Place::where('dbo:areaTotal', '>', 10000000000)->orWhere('dbo:country', Expression::raw('dbr:Italy'))->get();

Using Where In With An Array

$people = Person::whereIn('dbo:birthPlace', [Expression::raw('dbr:New_York_City'), Expression::raw('dbr:Philadelphia')])->get();

Using Where Between

$places = Place::whereBetween('dbo:areaTotal', [10000000000, 20000000000])->get();

Where null

This actually select elements for which an attribute is not set at all.

$places = Place::whereNull('dbo:areaTotal')->get();

Like

Acts like the "regex" operator, removing placeholder characters.

$people = Person::where('rdfs:label', 'like', '%Pacino')->get();

Order By

$places = Place::orderBy('dbo:areaTotal', 'desc')->get();

Distinct

Distinct requires a field for which to return the distinct values.

$places = Place::distinct()->get('dbo:country');

Distinct can be combined with where:

$people = Person::where('dbo:birthPlace', Expression::raw('dbr:New_York_City'))->distinct()->get('dbo:deathPlace');

Advanced Wheres

$places = Place::where('dbo:areaTotal', '>', 10000000000)->orWhere(function($query)
    {
        $query->where('dbo:country', Expression::raw('dbr:Italy'))
              ->where('dbo:areaTotal', '>', 10000000);
    })
    ->get();

Group By

Selected columns that are not grouped will be aggregated with the $last function.

$people = Person::groupBy('dbo:birthPlace')->get(['rdfs:label']);

Aggregation

$total = Place::count();
$max = Place::max('dbo:areaTotal');
$min = Place::min('dbo:areaTotal');
$avg = Place::avg('dbo:areaTotal');
$sum = Place::sum('dbo:areaTotal');

Aggregations can be combined with where:

$sum = Place::where('dbo:country', Expression::raw('dbr:Italy'))->sum('dbo:areaTotal');

Deleting

To delete a model, simply call the delete method on the instance:

$place = Place::first();
$place->delete();

Or deleting a model by its key:

Place::destroy('dbr:Philadelphia');

SPARQL specific operators

Regex

$people = Person::where('rdfs:label', 'regex', 'Pacino')->get();