effectiveactivism/sparql-client
OOP SPARQL 1.1 client (Symfony-focused) supporting SELECT/ASK/CONSTRUCT/DESCRIBE plus full update ops (INSERT/DELETE/REPLACE, graph management). Includes patterns, aggregates, functions, dataset clauses, validation, SHACL support.
composer require effectiveactivism/sparql-client
config/packages/sparql_client.yaml:
sparql_client:
query_endpoint: "http://your-sparql-endpoint/sparql"
update_endpoint: "http://your-sparql-endpoint/sparql"
use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
public function index(SparQlClientInterface $client) {
$result = $client->select(['?subject'])
->where([new Triple(new Variable('subject'), new PrefixedIri('schema', 'headline'), new PlainLiteral('Test'))])
->execute();
return $result->getRows();
}
$sparql = $client->select(['?article'])
->where([new Triple(new Variable('article'), new PrefixedIri('schema', 'type'), new PrefixedIri('schema', 'Article'))])
->limit(10);
$articles = $sparql->execute()->getRows();
$client->select(['?subject', '?predicate'])
->where([$triplePattern])
->orderBy([new Asc(new Variable('subject'))])
->limit(50);
private function getArticlesWithHeadline(SparQlClientInterface $client, string $headline) {
return $client->select(['?article'])
->where([
new Triple(new Variable('article'), new PrefixedIri('schema', 'headline'), new PlainLiteral($headline)),
new Triple(new Variable('article'), new PrefixedIri('rdf', 'type'), new PrefixedIri('schema', 'Article'))
]);
}
replace() for DELETE+INSERT:
$client->replace()
->delete([$oldTriplePattern])
->insert([$newTriplePattern])
->where([$filterTriple])
->execute();
$triples = [...]; // Array of Triple objects
$client->insert($triples)->execute();
$client->setExtraNamespaces(['foaf' => 'http://xmlns.com/foaf/0.1/']);
// Or per-statement:
$statement->setExtraNamespaces(['...']);
foreach ($result->getRows() as $row) {
$this->processRow($row);
}
SelectExpression for computed columns:
$countExpr = new SelectExpression(new Count(new Variable('subject')), new Variable('count'));
$client->select(['?subject', $countExpr])->where([...]);
try {
$client->beginTransaction();
$client->insert([...])->execute();
$client->commit();
} catch (\Exception $e) {
$client->rollback();
throw $e;
}
SELECT *: Explicitly list variables to reduce payload:
// Bad
$client->select(['*']);
// Good
$client->select(['?subject', '?predicate']);
LIMIT Early: Prevent memory issues with large datasets:
$client->select(['?subject'])->limit(1000)->where([...]);
WHERE clauses.$statement = $client->select([...])->where([...]);
$rawQuery = $statement->getQueryString();
$this->logger->debug('SPARQL Query:', ['query' => $rawQuery]);
200 for SELECT/ASK and 204 for updates.sparql_client:
query_endpoint: "https://secure-endpoint/sparql"
http_client:
base_uri: "%env(SPARQL_ENDPOINT)%"
auth_basic: ["%env(SPARQL_USER)%", "%env(SPARQL_PASS)%"]
TermInterface for domain-specific types:
class CustomIri implements TermInterface {
public function __toString() {
return '<' . $this->iri . '>';
}
// Implement other TermInterface methods
}
class LoggingSparqlClient implements SparQlClientInterface {
public function execute(StatementInterface $statement) {
$this->logger->info('Executing SPARQL:', ['query' => $statement->getQueryString()]);
return $this->delegate->execute($statement);
}
}
if (!filter_var($iri, FILTER_VALIDATE_URL)) {
throw new \InvalidArgumentException("Invalid IRI: $iri");
}
// Bad (ambiguous)
new PrefixedIri('schema', 'headline') // Assumes 'schema' is registered
// Good (explicit)
new PrefixedIri('http://schema.org/', 'headline');
409 Conflict responses gracefully:
try {
$client->execute($updateStatement);
} catch (HttpException $e) {
if ($e->getStatusCode() === 409) {
$this->handleConflict();
}
}
$validator = $client->getShaclValidator();
$validationReport = $validator->validate($triple, 'http://shacl/shapes/ArticleShape');
if (!$validationReport->isValid()) {
throw new \RuntimeException("SHACL validation failed: " . $validationReport->getMessage());
}
docker-compose for Blazegraph/Oxigraph:
services:
blazegraph:
image: effectiveactivism/sparql-blazegraph:latest
ports:
- "9999:9999"
volumes:
- ./data:/data
How can I help you explore Laravel packages today?