Using the API in php

Hi I’m from Niagara Falls Public Library and we are looking at making the jump from our own application that we developed in house, to Omeka S. I have a Debian server running in a VM and I’m just testing out the API.

I’m trying to follow along with the developer documentation. First I had an issue with finding the correct use statements, and now I’ve got a few errors;

Notice: Undefined variable: apiManager in /var/www/html/omeka-s/AddItem.php on line 10

Fatal error: Uncaught Error: Call to a member function execute() on null in /var/www/html/omeka-s/AddItem.php:10 Stack trace: #0 {main} thrown in /var/www/html/omeka-s/AddItem.php on line 10

Here’s the code;

  1. <?php
  2. require DIR.’/application/src/Api/Request.php’;
  3. use Omeka\Api;
  4. use Omeka\Api\Request;
  5. // Compose the request object
  6. $request = new Request(Request::READ, ‘items’);
  7. $request->setId(1);
  8. // Execute the request
  9. $response = $apiManager->execute($request);
  10. // Get the representation
  11. $item = $response->getContent();
  12. // Do something with the representation.
  13. echo json_encode($item);

You should not use Request objects: you should use directly ApiManager::read() method.

Where is your code located? You need to get ApiManager service.

In a factory, you get it using ContainerInterface::get('Omeka\ApiManager'). Then you inject it in your class through the constructor or a setter. Read more about factories.

In a view template, you can use $this->api() plugin which is a view helper which gets ApiManager service.

If you never worked with ZendFramework, I recommand you In-Depth Tutorial reading.

Sorry I was just trying follow along with the documentation that was made for the API. When I followed along with that In-Depth Tutorial reading it would coninually give a “Page not found. Reason: error-router-no-match”. It seems Omeka is different enough that the Zend documentation is not enough to get started. Working along with the Omeka S developer documentation does not seem like enough either.

Do you know if there if something will go wrong if I add these items directly to the database through insert statements? I’ve made a schema map and it doesn’t look as difficult as the tools provided.

It’s pretty tricky to manually edit the database through SQL statements, you should not do that.

I’ve created a Base module which you can easily edit to execute whatever you need.

EDIT: don’t forget to install your modules from Omeka Admin interface!

1 Like

We’ve definitely got improvements to make to the developer documentation, but I’m not totally clear on what it is you’re trying to do. @pols12 gives good advice about needing to get the API manager to use the API, but there’s more that would need to be changed from the example you’re giving…

The system isn’t really designed around having things work by just creating a new PHP file in the omeka-s folder; by doing it the way you’re trying to you’re skipping all the configuration and setup of the application and it’s just not going to work. Code that adds functionality to S generally will need to be in the form of a module.

If you’re just trying to use the API to insert/update items, you don’t need to use PHP or Omeka S code at all: the API is exposed as a REST API which you can interact with in whatever form you want.

This is our historical images database. As you might guess, it’s due for an upgrade. We also host multiple sites, like we had a seperate one for Canada’s 150th, and our local sports wall of fame.

Behind the scenes we have an xml/rdf exporter that will give the item information and the file location in namespaced tags. I was planning on using php to read the xml/rdf and add the items.

So you’re basically just looking to import. You can do this as a module, but it’d probably be more trouble than it’s worth, really.

Some easier options you might consider:

  • Transform your RDF into CSV and use the CSV Import module. If you have (or can have) your source files at some accessible URL this is very easy, or you can combine the CSV Import module with the File Sideload module and do direct sideloading of files from the local filesystem. As long as your data is basically amenable to transformation to a columnular/tabular format, this is probably the easiest option.

  • Use the REST API to create items. I see now that the code you started with was from our API documentation page… looking at it now I think it’s probably more confusing than helpful there. It’s supposed to be kind of an illustration of the type of code that runs internally, just to show that you can achieve the same results without that kind of internal code.

    The API documentation page doesn’t really provide you any code to start from because the idea is that you’re just making HTTP PUT/POST requests to the API endpoint of your install. You can use whatever language you want to make those requests. If PHP is what you’d prefer to work with, that works fine, but so does Python or anything else, really. You can do it with curl on the command line if you so choose.

    As with the previous option, if you’re working with files with accessible URLs, it’s a very simple process, just producing JSON for the item and POSTing it. True “uploads” are also possible with the API, they just require you to create a multipart message, which is usually slightly more difficult.

I’m thinking now that you were already looking to use the REST API so my “easier options” structure of this message doesn’t totally make sense, but oh well. Feel free to ask any further questions you have. We’re always open to improving the documentation (a good thing, since it clearly needs improvement!). A good first step might be for us to remove that misleading snippet.

Our data isn’t amenable to transformation to a tabular format,
so after building a custom conversion script this is an example of a JSON create item request;

{
“dcterms:identifier”: [{
“dcterms:identifier”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 10,
"@value": “http://www.nflibrary.ca/nfplindex/show.asp?b=1&ref=oo&id=102576
}],
“dcterms:title”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 1,
"@value": “Picking up American Wounded - Battle of Lundy’s Lane”
}],
“dcterms:creator”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 2,
"@value": “Campbell, Donna Marie”
}],
“dcterms:type”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 8,
"@value": “Still Image”
}],
“dcterms:medium”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 26,
"@value": “Watercolour”
}],
“dcterms:date”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 7,
"@value": “18140725”
}],
“dcterms:created”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 20,
"@value": “1975”
}],
“dcterms:isPartOf”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 33,
"@value": “Watercolour Collection”
}],
“dcterms:subject”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 3,
"@value": “War - War of 1812 - Battle of Lundy’s Lane”
}],
“dcterms:subject”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 3,
"@value": “Military”
}],
“dcterms:spaital”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 40,
"@value": “Canada”
}],
“dcterms:spaital”: [{
“type”: “literal”,
“lang”: “en-CA”,
“property_id”: 40,
"@value": “Canada - Ontario - Niagara Falls”
}]
}]
}

Does that look okay? It keeps sending back this error; “error”:“The API request resource must be a string. Type \u0022NULL\u0022 given.”} I can post my full code if needed.

I’m not sure what is meant to be used for main element. And php really does not like duplicate key names, but does omeka s take them via the api? And I wasn’t sure what to use for the top level element name. Ended up just using the same one in the xml I’m exporting from.

Also the next step will be adding the photo to the item, just want to make sure to add the item info first. I assume the api will return an item id when it’s created?

As an update, I used a PUT request over a POST, and I got an error;

Zend\View\Exception\RuntimeException
Zend\View\Renderer\PhpRenderer::render: Unable to render template “omeka/api/replace-list”; resolver could not resolve to a file

Details:

Zend\View\Exception\RuntimeException: Zend\View\Renderer\PhpRenderer::render: Unable to render template “omeka/api/replace-list”; resolver could not resolve to a file in /var/www/html/omeka-s/vendor/zendframework/zend-view/src/Renderer/PhpRenderer.php:497
Stack trace:
#0 /var/www/html/omeka-s/vendor/zendframework/zend-view/src/View.php(207): Zend\View\Renderer\PhpRenderer->render(NULL)
#1 /var/www/html/omeka-s/vendor/zendframework/zend-view/src/View.php(236): Zend\View\View->render(Object(Zend\View\Model\ViewModel))
#2 /var/www/html/omeka-s/vendor/zendframework/zend-view/src/View.php(200): Zend\View\View->renderChildren(Object(Zend\View\Model\ViewModel))
#3 /var/www/html/omeka-s/vendor/zendframework/zend-mvc/src/View/Http/DefaultRenderingStrategy.php(105): Zend\View\View->render(Object(Zend\View\Model\ViewModel))
#4 /var/www/html/omeka-s/vendor/zendframework/zend-eventmanager/src/EventManager.php(322): Zend\Mvc\View\Http\DefaultRenderingStrategy->render(Object(Zend\Mvc\MvcEvent))
#5 /var/www/html/omeka-s/vendor/zendframework/zend-eventmanager/src/EventManager.php(171): Zend\EventManager\EventManager->triggerListeners(Object(Zend\Mvc\MvcEvent))
#6 /var/www/html/omeka-s/vendor/zendframework/zend-mvc/src/Application.php(367): Zend\EventManager\EventManager->triggerEvent(Object(Zend\Mvc\MvcEvent))
#7 /var/www/html/omeka-s/vendor/zendframework/zend-mvc/src/Application.php(348): Zend\Mvc\Application->completeRequest(Object(Zend\Mvc\MvcEvent))
#8 /var/www/html/omeka-s/index.php(21): Zend\Mvc\Application->run()
#9 {main}

I assume that my duplicate keys are breaking the array that’s processing the request? I’m going to try creating a request without duplicates, then add those duplicates in as an update.

Since you’re creating you want to POST, not PUT. What URL are you trying to post/put to? That error you got is actually an interesting one we should handle more gracefully, but it doesn’t actually have to do with your payload, instead that you’re likely using the wrong URL.

The multiple duplicate keys issue: as you say PHP doesn’t deal with that natively in a nice way. Instead of duplicating you should utilize the fact that the value for each of those keys is actually an array. In other words, it’s this:

{
    "dcterms:whatever": [
        { ... },
        { ... }
    ]
}

and not

{
    "dcterms:whatever": [{
        ...
    }],
    "dcterms:whatever": [{
        ...
    }]
}

Other notes: you’ll want to use @language rather than lang, that’s part of the JSON-LD spec. There’s also no “top-level” key, unlike what would be required in, say, XML. Your top-level construct should just be an object, { } (see my simplified examples).

Thank you! I must of assumed JSON was like XML. The url that got the stacktrace was omeka-s/api. And the @language tip is very helpful. I was using the value table as a guide. So like this; ?

{
	"dcterms:spaital": [{
		"type": "literal",
		"@language": "en-CA",
		"property_id": 40,
                "@value": "Canada"
	}, {
		"type": "literal",
		"@language": "en-CA",
		"property_id": 40,
		"@value": "Canada - Ontario - Niagara Falls"
	}]
}

I ended up using php arrays anyway, so this is what json_encode() comes up with from this inside an item array that stores all the properties;

[dcterms:spaital] => Array
        (
            [0] => Array
                (
                    [type] => literal
                    [@language] => en-CA
                    [property_id] => 40
                    [@value] => Canada
                )

            [1] => Array
                (
                    [type] => literal
                    [@language] => en-CA
                    [property_id] => 40
                    [@value] => Canada - Ontario - Niagara Falls
                )

        )

Also if you know where I should start to authenticate properly that would also be a big help. I saw on this post you add it to the url like so ‘omeka-s/api/items?key_identity=blahBlah$key_credential=yadaYadaYada’

I ran into another one of those errors that you wanted to handle more gracefully POSTing to api/item/365 and api/items

Omeka S encountered an error
Omeka\Mvc\Exception\RuntimeException
POST request exceeded maximum size

Details:

Omeka\Mvc\Exception\RuntimeException: POST request exceeded maximum size in /var/www/html/omeka-s/application/src/Mvc/MvcListeners.php:368
Stack trace:
#0 /var/www/html/omeka-s/vendor/zendframework/zend-eventmanager/src/EventManager.php(322): Omeka\Mvc\MvcListeners->checkExcessivePost(Object(Zend\Mvc\MvcEvent))
#1 /var/www/html/omeka-s/vendor/zendframework/zend-eventmanager/src/EventManager.php(179): Zend\EventManager\EventManager->triggerListeners(Object(Zend\Mvc\MvcEvent), Object(Closure))
#2 /var/www/html/omeka-s/vendor/zendframework/zend-mvc/src/Application.php(311): Zend\EventManager\EventManager->triggerEventUntil(Object(Closure), Object(Zend\Mvc\MvcEvent))
#3 /var/www/html/omeka-s/index.php(21): Zend\Mvc\Application->run()
#4 {main}

That’s a different error… it just means the body of your request was bigger than the PHP post_max_size setting on your server (this is a php.ini setting, not an Omeka S setting).

The post_max_size setting was raised to 999M, and I’m only posting thumbnails as a test. I think that it’s having a issue because I’m making so many requests in a row?

The error is just about size. Can you confirm that the “System information” link at the footer of the Omeka S admin pages reflects your set POST size?

Basically this error is our attempt to detect the situation where the size is too large, but due to how PHP works this can only be detected indirectly, so it’s possible we’re picking up on something else you’re doing. What kind of request are you making when you try to do these POSTs? It should be a “multipart” request if you’re uploading files.

I’m creating the item, then using the o:id in the response to build the next ‘multipart/form-data’ POST request. I’m not sure if the media and file are meant to be in separate requests, but they are in the same one right now.

So, you can upload files (media) in the same request as the item, or a different one.

If you’re going for separate requests, then it would be a POST to api/media to do that second request to upload the file.

The latest version of Omeka S, 1.2.0, fixed a bug that would cause that “size exceeded” message to occur in situations when it shouldn’t. Perhaps that will help you out, though I think ultimately the issue is with the formatting of your request.

I’ve also made some changes to the API section of the developer documentation that should hopefully make it a little clearer. We still have more work to do there, though.