Giter Club home page Giter Club logo

contentful.php's Introduction

PHP

Join Contentful Community Slack   Join Contentful Community Forum

contentful.php — Contentful PHP Delivery Library

Packagist PHP version Packagist CircleCI

PHP library for the Contentful Content Delivery API and Content Preview API. It helps you to easily access your Content stored in Contentful with your PHP applications.

What is Contentful?

Contentful provides content infrastructure for digital teams to power websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enable developers and content creators to ship their products faster.

Table of contents

Core Features

Getting started

In order to get started with the Contentful PHP library you'll need not only to install it, but also to get credentials which will allow you to have access to your content in Contentful. This package requires PHP 7.2 or higher or PHP 8.0 or higher.

Installation

Install the library using Composer:

composer require contentful/contentful

Your first request

The following code snippet is the most basic one you can use to get some content from Contentful with this library: All interactions with the library go through Contentful\Delivery\Client. To create a new client an access token and a space ID have to be passed to the constructor.

$client = new \Contentful\Delivery\Client(
    'b4c0n73n7fu1', # This is the access token for this space. Normally you get both ID and the token in the Contentful web app
    'cfexampleapi' # This is the space ID. A space is like a project folder in Contentful terms
);

try {
    $entry = $client->getEntry('nyancat');
} catch (\Contentful\Core\Exception\NotFoundException $exception) {
    // Entry does not exist
}

Using this library with the Preview API

This library can also be used with the Preview API. In order to do so, you need to use the Preview API access token, available on the same page where you get the Delivery API token, and tell the client to use the different API:

$options = \Contentful\Delivery\ClientOptions::create()
    ->usingPreviewApi();
$client = new \Contentful\Delivery\Client($accessToken, $spaceId, $environmentId, $options);

You can find all available methods of our client in our reference documentation.

Authentication

To get your own content from Contentful, an app should authenticate with an OAuth bearer token.

You can create API keys using the Contentful web interface. Go to the app, open the space that you want to access (top left corner lists all the spaces), and navigate to the APIs area. Open the API Keys section and create your first token. Done.

Don't forget to also get your Space ID.

For more information, check the Contentful REST API reference on Authentication.

Documentation & References

Configuration

The ClientOptions class allows you to configure the client in a variety of different ways:

$options = \Contentful\Delivery\ClientOptions::create()
    ->usingPreviewApi()
    ->withDefaultLocale(string $defaultLocale = null)
    ->withHost(string $host)
    ->withLogger(Psr\Log\LoggerInterface $logger)
    ->withCache(Psr\Cache\CacheItemPoolInterface $cache, bool $autoWarmup = false, bool $cacheContent = false)
    ->withHttpClient(GuzzleHttp\Client $client)
    ->withoutMessageLogging()
    ->withQueryCache(Psr\Cache\CacheItemPoolInterface $queryCacheItemPool, int $queryCacheLifetime = 0)
;

$client = new \Contentful\Delivery\Client(
    string $accessToken,
    string $spaceId,
    string $environmentId = 'master',
    ClientOptions $options = null
);
Client parameter Default Description
$accessToken Required. Your access token
$spaceId Required. Your space ID
$environmentId 'master' Your environment ID
$options null A ClientOptions object
ClientOptions method Parameters Description
usingPreviewApi() - Use the Preview API host (preview.contentful.com)
withDefaultLocale() string $locale Set a locale to be automatically used for all requests
withHost() string $host A string to override the default Contentful API URL, useful if you have a proxy between your application and the Contentful API
withLogger() Psr\Log\LoggerInterface $logger A PSR-3 logger. Two types of logs are written: a generic one using either the INFO or ERROR level (depending on the response status code) with a brief summary, and a complete dump of request and response using the DEBUG level. We suggest to configure the logger minimum level according to your needs.
withCache() Psr\Cache\CacheItemPoolInterface $cache A PSR-6 cache item pool. This will be used to stored data such as content types and locales, which are always needed but don't change often
withCache() bool $autoWarmup = false When using a cache pool, set this to true to automatically fill the cache during regular use
withCache() bool $cacheContent = false When using a cache pool with $autoWarmup set to true, se this to true to fill the cache with entries and assets during runtime. This may speed up execution when calling $client->getEntry($entryId) and $client->getAsset($assetId), but not when calling the getEntries() and getAssets() methods, as the client can't reliably know which entries or assets will be returned by the API, and for this reason the cache can't intercept the call.
withHttpClient() GuzzleHttp\Client $client A Guzzle client instance, which can be configured with custom middleware
withoutMessageLogging() - Do not store API requests and responses (which can use a lot of memory). If messages are not stored, they will not be retrievable from Client::getMessages() for debugging and inspection purposes
withQueryCache() Psr\Cache\CacheItemPoolInterface $queryCacheItemPool A PSR-6 cache item pool. This will be used to cache items retrieved with queries when calling $client->getEntries($query).
withQueryCache() int $queryCacheLifetime = 0 The number of seconds of lifetime for $client->getEntries($query) cache items. There's no invalidation mechanism on these cache items so consider to set a low lifetime (for example 60 seconds).

Reference documentation

The PHP library reference documents what objects and methods are exposed by this library, what arguments they expect and what kind of data is returned.

Most methods also have examples which show you how to use them.

Tutorials & other resources

  • This library is a wrapper around our Contentful Delivery REST API. Some more specific details such as search parameters and pagination are better explained on the REST API reference, and you can also get a better understanding of how the requests look under the hood.
  • Check the Contentful for PHP page for Tutorials, Demo Apps, and more information on using PHP with Contentful.

Upgrade

For details about how to upgrade from version 3.x to version 4, please check the changelog entry for version 4.0.0 and the upgrade to version 4 guide.

For details about how to upgrade from version 2.x to version 3, please check the changelog entry for version 3.0.0 and the upgrade to version 3 guide.

Reach out to us

You have questions about how to use this library?

  • Reach out to our community forum: Contentful Community Forum
  • Jump into our community slack channel: Contentful Community Slack

You found a bug or want to propose a feature?

  • File an issue here on GitHub: File an issue. Make sure to remove any credential from your code before sharing it.

You need to share confidential information or have other questions?

  • File a support ticket at our Contentful Customer Support: File support ticket

Get involved

PRs Welcome

Important: Right now, the API has php-vcr as a development dependency, which does not officially support PHP8 yet. If you want to develop on PHP8, you will need to install the dependencies with composer install --ignore-platform-reqs to overwrite this requirement.

License

This repository is published under the MIT license.

Code of Conduct

We want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.

Read our full Code of Conduct.

contentful.php's People

Contributors

brettmc avatar cf-allstar[bot] avatar dborsatto avatar fabianaromagnoli avatar ghepting avatar haehnchen avatar kwivix avatar lucasmichot avatar magnusnordlander avatar marcolink avatar mariobodemann avatar matthew-contentful avatar mmenozzi avatar o0-zzuf-0o avatar pgrigoruta avatar realityking avatar sebb767 avatar slootjes avatar smillerdev avatar yadakhov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

contentful.php's Issues

How to query for empty value on URL field

Hello,

I have a Contentful field of type 'short text' that's a URL field and I'm trying to get results where my field "externalUrl' is empty. I've tried many different things but can't seem to exclude items where this field is filled out. Should this work to find items where my 'externalUrl' field is empty?

$query->where('fields.externalUrl', '');
I've also tried
$query->where('fields.externalUrl[exists]', false);

This is my full query...
$query = new \Contentful\Delivery\Query;
$query->select(['fields.slug']);
$query->setContentType('entries');
$query->where('limit', 1);
$query->where('fields.publishedDate[lt]', $date);
$query->where('fields.externalUrl', '');
$query->orderBy('-fields.publishedDate');
$query->setInclude(1);

The query still returns items where the externalUrl is filled out.

Call to a member function getFile() on null

Hey,

I have a basic content type with title and an image:

qsfitgc5ni

If I want to loop through all images everything is fine:

$assets = $client->getAssets();
foreach($assets as $asset) {
    print_r($asset->getFile()->getUrl()); // works
}

But if I want to loop through all entries, I don't have access to getFile().

$entries = $client->getEntries();
foreach($entries as $entry) {
    print_r($entry->getTitle()); // works
    print_r($entry->getAvatar()); // Contentful\Delivery\Asset Object
    print_r($entry->getAvatar()->getFile()->getUrl()); // don't work: Call to a member function getFile() on null
}

What I'm doing wrong here?

Call to undefined (magic) method

Hi,

We have a field in Contentful with the id 'echoId'. When we call '$entry->getEchoId()' we get a "Call to undefined method Contentful\Delivery\DynamicEntry::getEchoId()"

You can see this error here: http://staging.api.bmlabs.org/api/entry/1115

Why would this one magic method/field name cause an error?

We tested against: echo, testId, and other fields, and we've not come across this issue anywhere else.

Thanks,

Will

Implement ArrayAccess on the DynamicEntry

It would be nice to implement ArrayAccess on the DynamicEntry (as perhttps://github.com/usemarkup/contentful/blob/master/src/DynamicEntry.php) so that the entry can be treated as an array (for templating language less forgiving than twig)

Original comment from @calumbrodie: #20 (comment)

buildFile() in ResourceBuilder not getting the correct array if language is set

The line
https://github.com/contentful/contentful.php/blob/master/src/Delivery/ResourceBuilder.php#L204

causes that the method buildFile() gets the wrong array.

buildFile() tries to access the array element 'details'

        $details = $data['details'];

which is available one step deeper in the array. The structure of the array $data in this case is

Array
(
    [de-DE] => Array
        (
            [url] => //images.contentful.com/...
            [details] => Array
                (
                    [size] => 4945457
                    [image] => Array
                        (
                            [width] => 6000
                            [height] => 3304
                        )

                )

            [fileName] => 
            [contentType] => image/jpeg
        )
)

A possible fix would be to pass the correct array to the buildFile() method, which means to get rid of the first layer of the array like:

Array
(
    [url] => //images.contentful.com/.../02979f4779d7c55d9aad665d6a6bfeec/H__hnerei_bearb.jpg
    [details] => Array
        (
            [size] => 4945457
            [image] => Array
                (
                    [width] => 6000
                    [height] => 3304
                )

        )

    [fileName] => 
    [contentType] => image/jpeg
)

This works for our case:

$normalizedFieldData = $this->normalizeFieldData($fields['file'], $locale);
if ($locale && isset($normalizedFieldData[$locale]) && count($normalizedFieldData[$locale]) > 0) {
    $normalizedFieldData = $normalizedFieldData[$locale];
}
$files = isset($fields['file']) ? array_map([$this, 'buildFile'], $normalizedFieldData) : null;

Add support for checking a field exists at all

Currently the magic __call() method is basically used for magic getters, i.e. getName(). When dealing with content types that have optional properties it would be beneficial to have a hasName() equivalent which would just check if the thing exists at all.

CDN host vs API host

Using PHP SDK for Delivery API I've got an old version of a content. For example in the Contentful a content has a version 20, but from the CDN I've got a version 3. So then when I used that version to use Management API I've got 409 Conflict error with id 'VersionMismatch'. Probably the reason of that is CDN host (cdn.contenful.com) because when I used API host (api.contentful.com) by hand then I've got a correct version of a content.

In the current implementation it's not possible to change the host for API, but maybe it's not necessary and I'm doing something wrong. Can anybody help me with that?

DeletedEntry should also have a getContentType() method

Currently DeletedEntry payloads look like this:

{
  "sys": {
    "type": "DeletedEntry",
    "id": "someid",
    "space": {
      "sys": {
        "type": "Link",
        "linkType": "Space",
        "id": "somespace"
      }
    },
    "revision": 3,
    "createdAt": "2017-05-03T13:19:33.711Z",
    "updatedAt": "2017-05-03T13:19:33.711Z",
    "deletedAt": "2017-05-03T13:19:33.711Z",
    "contentType": {
      "sys": {
        "type": "Link",
        "linkType": "ContentType",
        "id": "theme"
      }
    }
  }
}

Yet the DeletedEntry class doesn't have the getContentType() method that e.g. DynamicEntry has. This makes it impossible to determine what type of thing was deleted, you have to parse the raw payload yourself to extract the data.

Are you accepting pull requests? There doesn't seem to be much activity here so I'm reluctant to post a solution myself, but if you acknowledge this then I will.

Mock Client Call

In order to write independent test, I want to mock Delivery/Client and then fake the return without actually hit Contentful. But I don't see any way to do it.

Could you please give me a suggestion?

[Question] Possible to avoid querying Space & ContentType when querying Entries?

When fetching entries, sometimes I want just the entry without resolving any of it's Links.

In particular, the sdk, by default, will query Contentful to resolve the space and contentType properties in the SystemProperties for a single entry when doing a simple getEntries call (via ResourceBuilder:: buildSystemProperties).

Note that setting includes to 0 does not affect this behaviour.

We don't need that data at this point and would like control over when we get that data.

It means that, if I'm not mistaken, 3 requests are sent to Contentful to resolve a single entry with 0 includes. For us, this is slow and inefficient.

For context, in our use case, we are instantiating many separate Content Delivery Clients as part of a background job (via a queue system). Each Client is getting some standard entries (via getEntries). Because we are making many of these Clients, the SDK is forcing many superfluous requests. The InstanceCache is not shared between clients so does not help.

Is it possible to avoid this default behaviour, and just keep the space and contentType as Links?

Memory exhaustion caused by the buildLink method

The code is stuck in what seems to be an infinite loop which causes a memory exhaustion when getting resources from the resource builder.

The issue seems to come from the buildLink method in the ResourceBuilder class (vendor/contentful/contentful/src/Delivery/ResourceBuilder.php)

Commenting out the lines below fix the issue:

            if (isset($rawDataList['entry'][$id]) && $depthCount < 20) {
                $depthCount++;
                return $this->buildEntry($rawDataList['entry'][$id], $rawDataList, $depthCount);
            }

Is there a fix for that?

Asset get() methods can throw Uncaught Exceptions when the content model is non-localized

Asset methods such as getFile() and getTitle() can throw uncaught exceptions when the Asset in question has localized property set to false.

For example, say i have the regular DynamicEntry - Car, with a linked field Image, which is an Asset.

Let's say the Image looks like this:

  {
    "name": "Image",
    "id": "image",
    "type": "Link",
    "linkType": "Asset",
    "validations": [],
    "localized": false,
    "required": false,
    "disabled": false,
    "omitted": false
  }

Now, if you attempt to getTitle('fr') on this Asset for example, the SDK will throw:

Undefined property: stdClass::$fr

I'd suggest the expected behaviour in this scenario should be fallback to the default value.

Undefined function curl_reset() on GuzzleHttp

When trying $client->getEntries();

I'm getting this error:
Call to undefined function GuzzleHttp\Handler\curl_reset() in ../guzzlehttp/guzzle/src/Handler/CurlFactory.php on line 78

Reinstalled all libraries related to contenful to no avail.

Thanks

Using magic methods to get field values breaks PHP conventions

PHP functions and method names are case insensitive, so it is unidiomatic for the SDK classes to rely on specific capitalization when getting field values. At worst, this can lead to fatal errors, and at best it is opaque to force the developer to guess how you might be manipulating the field name behind the scenes.

Let's assume that I am fetching entries of a content type named "my_thing" that has a field named "seo_copy".

$client = new \Contentful\Delivery\Client($accessToken, $spaceId);

$query = (new \Contentful\Delivery\Query())
    ->setContentType('my_thing')
    ->where('fields.slug', 'xyz');

$entries = $client->getEntries($query);

foreach ($entries as $entry) {
    print $entry->getSeo_Copy() . PHP_EOL; // fatal error
    print $entry->getSeoCopy() . PHP_EOL; // fatal error
    print $entry->getSEO_COPY() . PHP_EOL; // fatal error
    print $entry->getSeo_copy() . PHP_EOL; // this works
    print $entry->getseo_copy() . PHP_EOL; // this also works
}

Expected Outcome:

I should be able to retrieve my field value no matter how I have capitalized my function name. Calling any other PHP function or method is not case sensitive. print == PRINT == pRiNt == PRInt etc.

Actual Outcome:

The method name capitalization matters. Using a variation that is not supported results in a fatal error like the following:

Fatal error: Call to undefined method Contentful\Delivery\DynamicEntry::getSeoCopy() in vendor/contentful/contentful/src/Delivery/DynamicEntry

Recommendation:

It would be more consistent and require less vetting code in the DynamicEntry's __call method if the class provided a get($field) method that accepts the exact field identifier. Likewise, you could wire this up to use a magic __get() method to retrieve object properties (and PHP variable and properties are case sensitive, so this would not have the same problem of running afoul of known PHP conventions/limitations). Alternatively, if field IDs in Contentful are not case sensitive, then the getter functions could normalize field IDs prior to looking for values (e.g. make all field identifiers lowercase).

Setinclude not including resolved nested reference fields

Hello, this may not be an issue, but my misunderstanding of the documentation. I'm using SDK version 2.4.1 still.

I have a content type that has a reference field. The entries in that reference field also have their own reference field with entries inside.

My understanding of setInclude is that if I set it to higher than 1, like 10, it should not just give back the ID's of these nested reference fields, but also bring back the resolved data all in one query.

This is my query...

$query = new \Contentful\Delivery\Query;
$query->select(['fields.title', 'fields.description', 'fields.coverModule', 'fields.modules']);
$query->setContentType('homepage');
$query->setInclude(10);
$homepage = $this->app->contentful->getEntries($query);

When I echo json_encode($homepage), I get the following output below, which brings back only the IDs, not any resolved data. With that said, the API does work as expected and I can call .get[ProperyName] on all the nested items, but I have the sense that the API is doing further queries to resolve those, because my PHP write time is going to 1-2 seconds to loop through the entries and grab nested properties from the reference fields.

{
    "sys":{
        "id":"2H9Bwvds0ossag0uaO660c",
        "type":"Entry",
        "space":{
            "sys":{
                "type":"Link",
                "linkType":"Space",
                "id":"5jh3ceokw2vz"
            }
        },
        "contentType":{
            "sys":{
                "type":"Link",
                "linkType":"ContentType",
                "id":"homepage"
            }
        },
        "revision":46,
        "locale":"en-US",
        "createdAt":"2017-11-12T19:40:15.980Z",
        "updatedAt":"2018-11-11T22:11:19.446Z"
    },
    "fields":{
        "title":"Homepage",
        "description":"description copy",
        "coverModule":{
            "sys":{
                "type":"Link",
                "linkType":"Entry",
                "id":"4663P86Jp6SMsQkKeyS0EO"
            }
        },
        "modules":[
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"6PvlQ2HeJUMKgWsICs8G8Q"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"3wrlDxJVywc02WewSAkQiS"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"5Th37DorHq0YmaO86KO44K"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"6l0pD3XjPySCMcWAqmkaWg"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"2ZglLYtuisiSMg62a0MoMS"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"41cz2nwQYECmKaYEMsgsa"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"3PBQzRRwlOo4QMKKYSg8Ay"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"5nmzfkakaka2QaUgGWuiE8"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"5EK8SD5jJmsui0UIIIyQuW"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"6IqiFYLSBaSAU2wsYEaAUC"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"wI5KNz8OZwAWmYouGo6eM"
                }
            },
            {
                "sys":{
                    "type":"Link",
                    "linkType":"Entry",
                    "id":"zhgR4exjNYYe2Mc08Ao8a"
                }
            }
        ]
    }
}

Grabbing linked content

Is there a way to actually display "includes" from a request?

Thus far if I have a content entry with references inside, there is no apparent way in which to display these it would seem...

i.e.:

$query = new \Contentful\Delivery\Query();
$query->where('include', 3);
$query->where('sys.id', 'someId');
return $client->getEntries($query);

or am I going about this incorrectly?

Difficulty with query

Hello, I want to query all entries of the type "store" that have in the field "category" a link to an specific "id" how can I achieve this?

I tried that:

$query = new \Contentful\Delivery\Query;
$query->setContentType('store')->where('fields.store.pt-BR', $id );
$page = $client->getEntries($query);

But didn't work.

Locale bug noticed

If you were to set the locale for a field that hasn't got a locale assigned, when using something like say $post->getTitle(), it would yield Undefined property: stdClass::$ru.

Is there a way of determining if that field has said locale, because if not it would appear the line: 102 in DynamicEntry is the culprit.

Thanks,

Michael

Releases without '-beta' suffix?

According to SemVer you're already allowed to make breaking API changes in a next 0.x release. So, the '-beta' suffix should be unnecessary.

As an added bonus you don't have to specifically add this package if you already have a Composer dependency on for instance the ContentfulBundle for Symfony, which would improve the Developer Experience and you'd be able to better manage the dependency of the bundle on a specific version of this package.

Could you please consider this change in your release cycle?

Synchronization Query always performs an initial synchronization

The getQueryData method in the Synchronization Query object looks like this:

public function getQueryData()
{
    $data = [
        'initial' => true,
        'type' => $this->type !== 'all' ? $this->type : null,
        'content_type' => $this->contentType
    ];

    return $data;
}

This method is called in the Manager as following:

public function startSync(Query $query = null)
{
    $query = $query !== null ? $query : new Query;
    $response = $this->client->syncRequest($query->getQueryData());

    return $this->buildResult($response);
}

As you can probably see, this code can not be extended in it's current form. There is a way to inject a custom Query object, but there is no way to override the initial state.

The problem with this is that it is not possible to performance delta synchronization at all; every sync will return the entire resultset as if it were new.

Human documentation

There's only generated API docs, which basically says as much as my IDE does. It would be nice to have some documentation which illustrates the different use-cases of the SDK as well as the conventions around it. The project I'm currently working on uses contentful but we're hesitant to use the provided SDK because the lack hereof.

Content Type and Space API Requests

Problem

When I simply want a list of entries or just an entry I never need the Space or Content Type info. Yet the API loads this data no matter what. It seems somewhat tied to some aspects of the DynamicEntry setup but leads to unnecessary API calls compounding.

For instance, if I am to make an entries call to something that has a few linked content types nested, then this one API request could lead to a count of the unique content types being pulled plus the space info.

These requests are fast, typically, but they add up and seem avoidable, or could be part of the main API response if they are indeed necessary.

Solution

No idea.

I could override the Client::getEntries method and return the raw response by just using the Client::request() instead of Client::requestAndBuild but then I lose the things that are helpful about the DynamicEntry class. Things like locale help, and loading the nested objects.

From what I can tell it's not possible to build out a DynamicEntry without the Content Type and Space metadata.

So could the API actually return all this metadata (kind of like we get with the includes param)?
Or can we see a way to do things without forcing those calls?

Thanks for any ideas or feedback.

@realityking Pinging you, since you are visiting next week.

Undefined index: "fi-FI" (or similar) when attempting to retrieve a non-existing optional property

https://github.com/contentful/contentful.php/blob/master/src/Delivery/DynamicEntry.php#L138 will issue an undefined index notice if you call $entry->getPartner() on a DynamicEntry made from this piece of JSON:

{
  "sys": {
    "space": {
      "sys": {
        "type": "Link",
        "linkType": "Space",
        "id": "spaceid"
      }
    },
    "type": "Entry",
    "contentType": {
      "sys": {
        "type": "Link",
        "linkType": "ContentType",
        "id": "theme"
      }
    },
    "id": "someid",
    "revision": 3,
    "createdAt": "2017-04-18T12:15:23.567Z",
    "updatedAt": "2017-05-03T11:23:04.368Z"
  },
  "fields": {
    "title": {
      "fi-FI": "Some theme"
    },
    "urlSlug": {
      "fi-FI": "some-theme-slug"
    },
    "category": {
      "fi-FI": {
        "sys": {
          "id": "someid",
          "linkType": "Entry",
          "type": "Link"
        }
      }
    },
    "description": {
      "fi-FI": "Such theme, much something"
    },
    "image": {
      "fi-FI": {
        "sys": {
          "id": "someid",
          "linkType": "Asset",
          "type": "Link"
        }
      }
    },
    "partner": {}
  }
}

If "partner": {} is removed, everything works as expected and getPartner() will return NULL. The partner field in this case is an optional non-localized reference.

This effectively means it's impossible to check if a partner has been specified without triggering a notice, which in many cases are converted into exceptions. I filed #102 as a potential workaround for this.

Locale fallbackCode should not be required

In ResourceBuilder.php on line 458, the following code causes an "undefined index" error:

$locales[] = new Locale($locale['code'], $locale['name'], $locale['fallbackCode'], $locale['default']);

This is due to the fact that fallbackCode does not always exist, in which case, the code should default to something else.

Documentation: Setting language as a fallback

Description

I've been trying to access values of translated fields using magic methods, but am running into issues. For context, I am using the most recent version of this SDK with Laravel (though I dropped using contentful/laravel package and made my own service provider because of minimum requirements of this package).

This issue occurs whether I setLocale on a new Query or passed to getEntry like below.

Code

$client = new Client(..., ..., ..., null, null, 'en-US');
$entry = $client->getEntry($id, 'de');

dd($entry);

Spits out this content:

If I take the same code and try and grab the contentBlocks:

dd($entry->getGameAssets());

It gives me this:

What I Expect

getGameAssets() should always have access to en-US since it's my fallback language and I do have translations for that locale

Also occurs when using Query

$query = (new Query)->setLocale('de');
$query->where('sys.id', $id);
$entry = $this->client->getEntries($query)[0];

dd($entry->getContentBlock());

Entry not cached?

compose.json

{
    "require": {
        "contentful/contentful": "^4.1",
        "symfony/cache": "^4.2",
        "symfony/var-dumper": "^4.2"
    }
}

index.php

<?php

require_once 'vendor/autoload.php';

$cache = new \Symfony\Component\Cache\Adapter\FilesystemAdapter('', 0, 'cache');

$clientOptions = \Contentful\Delivery\ClientOptions::create()->withCache($cache, false);

$client = new \Contentful\Delivery\Client('XXXXXX', 'XXXXXX', 'master', $clientOptions);

//$entries = $client->getEntries();

$entry = $client->getEntry('1pk6PmEtDSQUSoGWACyGwa');

echo $entry->getLocale();

I can see the cache is built, but I am still seeing HTTP call, why ?

Single locale is ignored when lazy loading a linked entry

This should be true but it's false:

<?php
$client = new \Contentful\Delivery\Client('b4c0n73n7fu1', 'cfexampleapi');

$happycat = $client->getEntry('happycat', 'tlh');
var_dump($happycat->getBestFriend()->getName() === 'Nyan vIghro\'');

Add ability to update Contentful's source CDN

Probably crappy issue title, but it'd be great to able to customize the source URL of the CDN from which to fetch data.

Use case

We're wary of hitting rate limits, so we'll periodically query content and cache the data on our end at a custom URL (our own CDN). I couldn't customize this library in an expected way (extend and override parent constructor) because variables were private.

How I got around this

I copied and overrode Contentful\Delivery\Client and updated the base URI there.

Possible solutions

  1. Switch all private variables to protected so child Classes can override them (Refs #7)
  2. Allow passing a custom URL to serve as a CDN (Added benefit of being able to easily stub data for testing)

Proposed syntax for option 2

Pass a seventh (starting to get long, I know) parameter to override the base URI.

new Client(config('contentful.token'), config('contentful.space'), config('contentful.preview'), null, null, 'en-US', 'https://cdn01.domain.com/spaces`);

Disclaimer

I can see why this might be rejected, since we're technically grabbing this from a separate source which Contentful doesn't own, but can see how this might account for future custom regions/CDNs from Contentful.

Don't use private properties on the Delivery Client

I'm currently trying to integrate the Contentful SDK into a Laravel application and want to extend the Delivery Client and Resource Builder objects in order to make them return custom objects instead of the ResourceArray and DynamicEntry objects.

This requires extending the DeliveryClient to create a custom Builder, which is not possible because of the fact that the $builder, $instanceCache and $preview properties are private.

Please make these properties protected so that we can build on them :)

An alternative, in my opinion even better, solution would be implement createResourceBuilder() and createInstanceCache() methods that can be extended.

Memory leak introduced some time after version 2.x

We've tried two times to upgrade the version of this SDK that we use (first from 2.x to 3.x, now from 2.x to 4.x) but both times we're run into memory leaks that have turned out to be showstoppers.

Here's what it looks like when we attempt to synchronize all assets from a Contentful space:

$ php artisan contentful:assets:sync --ignoreExisting -vvv
Synchronizing assets/media...
 32300/43028 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░]  75% 4 mins/6 mins 256.0 MiB

PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to allocate 122880 bytes) in /vagrant/api/vendor/guzzlehttp/psr7/src/Stream.php on line 94

On 2.x it used to look like this:

$ php artisan contentful:assets:sync --ignoreExisting -vvv
Synchronizing assets/media...
 43029/43029 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 3 mins/3 mins 22.0 MiB

Done, synchronized 43029 assets

As you can see the memory usage is supposed to remain fairly constant (it started out at 20.0 MiB).

We're using the console commands provided by this library: https://github.com/digiaonline/lumen-contentful-sync/ (tested using a WIP branch that supports the latest SDK: digiaonline/lumen-contentful-sync#15).

Even if I strip all our own processing logic for the loop the memory leak persists, which further confirms my suspicion that the SDK is somehow at fault. Here's the gist of the loop:

$this->output->progressStart($this->getClient()->getAssets($this->getTotalQuery())->getTotal());

do {
    $assets = $this->getClient()->getAssets($this->getQuery());

    // Process the current batch
    foreach ($assets as $asset) {
        // SNIP: Processing would normally happen here
        $this->numSynchronized++;

        $this->output->progressAdvance();
    }

    // Move on to the next batch
    $this->skip += $assets->getLimit();
} while ($this->skip < $assets->getTotal());

$this->output->progressFinish();

The full source for the command can be read here: https://github.com/digiaonline/lumen-contentful-sync/blob/71b661373d4ab152aa245b6872405ff215c3b285/src/Console/Commands/SyncAssetsCommand.php

Release date of 0.7.x?

Hey guys,

RE this ticket - - when is 0.7.x going to be released so that we can have a workable solution surrounding fields that may have "Id" in their field names?

#9

Thanks,

Michael

Field IDs with first letter uppercase don't work.

If someone needs (or wants) to create field Ids with the first letter upopercase, this SDK will not work since here https://github.com/contentful/contentful.php/blob/master/src/Delivery/DynamicEntry.php#L94 the first letter will alway be converted to lowercase.

Two options come to my mind:

  • Either Prepare the field Ids when they come so that the first letter is always lowercase.
  • Extend the above line to handle Uppercase fieldIds too.

Edit: If someone wonders: Yes, you can create field Ids like this in Contentful: MetaDescription, PageTitle

Greets Marcus

Triggered error after requesting the value of an optional parameter.

I'm using the Contentful-Bundle but I think the problem is inside the core.

We have some optional parameters in Contentful, like "content" or "excerpt". When we now retrieve our entries and iterate over them, we want to check if the property is defined before outputting it. So let's say we have one entry with no content, but an excerpt

{% for entry in content.entries %}
    <article>
    {% if entry.getExcerpt() %}
        <p>{{ entry.getExcerpt() }}</p>
    {% endif %}

    {% if entry.getContent() %}
        <h3>{{ entry.getHeadline() }}</h3>
    {% endif %}

The first if works without problems but the second triggers the error trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR); in Contentful\Deliver\DynamicEntry in line 135. If I replace this line with return null it works without problems. It seems that the API doesn't let us check for optional parameters.

What would we need to do to make this possible?

Unset field throws error in src/Delivery/ResourceBuilder.php

  1. Create an asset (A), type in the title but do not attach a file. Save as draft
  2. Associate this asset as a reference in another content item (B)
  3. Use SDK to query for this item (B)
  4. Exception thrown on line 208, triggered by call on line 189 where $fields['file'] is not set

[Question] How to get a single entry by field value

Hello,

As far as I have seen, you can get an entry by its id

$entry = $client->getEntry('entry-id');

I have tried to access the fields with the where query method this way:

$query = new \Contentful\Delivery\Query;
$query->where('fields.slug', $slug);
$entries = $client->getEntries($query);

I have tried with different approaches like fields.slug.es-ES but all I get is a 400 Bad Request response, that says that the Content Type ID is required.

My question is: How can I get a single entry by a field value?

Thank you.

$client->getEntries() does not work.

require_once 'vendor/autoload.php';

$client = new \Contentful\Delivery\Client('b4c0n73n7fu1', 'cfexampleapi');
$entries = $client->getEntries();

Output:

Catchable fatal error: Argument 1 passed to Contentful\Delivery\Client::getEntries() must be an instance of Contentful\Query, none given, called in /Users/stephen/dev/contentful-php-example/quickstart.php on line 5 and defined in /Users/stephen/dev/contentful-php-example/vendor/contentful/contentful.php/src/Delivery/Client.php on line 151

setInclude not working

Hi,

I was just trying to query our account using this PHP API wrapper. Querying by content_type seems to work fine, but for some reason fails with entry links. Below is a snippet of my usage:

        $query = (new Query())
            ->setContentType('topic')
            ->where('fields.title', $request->getAttribute('title'))
            ->setInclude(2);
        $videos = $client->getEntries($query);

The include option works fine if I build the request myself and fetch the data using cURL.

Any ideas what's wrong? Not sure if I'm just not using the wrapper correctly.

Thanks!

Content Management API

I've seen that this repository only supports Content Delivery API.
Are there plans for introducing Content Management API and Content Preview API?

If you want I can help to implement them.

Thank you.

Caching the API Calls

Hi,

Just wondering if object caching is happening at all with the PHP SDK? If yes, at which version it is available? I've read the version logs but I just wanted to make sure here.

My api calls are going to the sky (not just roof). I suspect it is because bots are hitting non-existent slugs and goes to 404 with query requests (for menu navigations).

I checked the code and there is a term of "WarmingUp the Cache" and supposed to be used with the cli command. Is there an example of this object/space caching with php?

Thank you for the answer.

Adi

How to perform full-text or field-based searching?

The CF Delivery API docs state:

It's possible to perform a full-text search across all text and symbol fields with the query parameter.

You can perform a full-text search on a specific field with the [match] operator.

I can't see a way to take advantage of this using the PHP SDK as it exists. Is that correct? Maybe I'm not understanding how to use magic methods to support params/operators which don't have an explicit PHP method defined.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.