Cms: ElementQuery::site/siteId: Allow multiple?

Created on 7 May 2018  路  22Comments  路  Source: craftcms/cms

Unless I'm missing something - it would seem it is impossible to select elements from multiple sites with ElementQuery.

Both site and siteId seem to only accept scalar values, and passing null seems to only select the default/current site elements.

Craft CMS 3.0.5

enhancement site development

Most helpful comment

Just pushed up the 3.2 branch, which implements this FR.

To update to it, change your craftcms/cms requirement in composer.json to:

"require": {
  "craftcms/cms": "^3.2.0-alpha.1",
  "...": "..."
}

Then run composer update.

As of d01702278bd84b84392e11da72aa4d0b6f4f0943 you can pass multiple site handles to the site element query param (or multiple site IDs to the siteId param). You can also pass site('*') to query across all sites, or site(['not', 'siteHandle1', 'siteHandle2']) to exclude specific sites.

When you query across multiple sites, any elements that are enabled for multiple sites will be returned multiple times (once per site). This is great for cases when you want to create a language picker and get all of the localized versions of an entry at once. However there are cases where that wouldn鈥檛 be desired, so we鈥檝e also added the unique param (5a280602fc5c366d47702361652505a5f3ad99bb), which can be used in conjunction with site, which will filter out duplicate elements from the results.

{% set entries = craft.entries()
    .section('news')
    .site('*')
    .unique()
    .all() %}

All 22 comments

Correct. I guess I should take this as a feature request?

It'd be nice to be able to pass in null to get Elements from all sites that match the criteria, or to be able to pass in the site or siteId as an array, imo

Correct. I guess I should take this as a feature request?

Yep, guess so.
I wasn't 100% of the intended behavior given then description of the site param (the site(s)鈥檚 part):

Sets the siteId param based on a given site(s)鈥檚 handle

Doh, that is a doc bug; fixed for the next release. Just as you couldn鈥檛 select elements from more than one locale in Craft 2, you can鈥檛 (currently) select elements from more than one site at a time in Craft 3. Will look into what it would take to change that, though.

This request was posted in May. Is it being developed or do I need to look for other solutions to solve this problem?

Not currently being developed, no.

I proposed the ideas on my accidental duplicate of the following.

Entry::find()
    ->section('businesses')
    ->site(['siteHandle1', 'siteHandle2', 'siteHandle3'])
    ->orderBy('title asc')
    ->limit(100)
    ->anyStatus();
Entry::find()
    ->section('businesses')
    ->siteGroup(['siteGroupHandle1', 'siteGroupHandle2'])
    ->orderBy('title asc')
    ->limit(100)
    ->anyStatus();

Damn, I need this too now :D

I also need this!

I needed this too so I've used the Twig array merge filter for the interim along with the excellent superSort plugin to sort the merged arrays back into chronological order.

Here's a simplified version:

```
{% set multiSiteEntries = [] %}

{% set sites = craft.app.sites.getAllSites() %}
{% for site in sites %}
{% set multiSiteEntries = multiSiteEntries|merge(
craft.entries().
siteId(site.id)).
section('blog').
all())
%}
{% endfor %}

Yup. I鈥檝e discovered that we need the ability to run an entries query across multiple Sites. Where is the preferred place to add feature requests these days?

Just pushed up the 3.2 branch, which implements this FR.

To update to it, change your craftcms/cms requirement in composer.json to:

"require": {
  "craftcms/cms": "^3.2.0-alpha.1",
  "...": "..."
}

Then run composer update.

As of d01702278bd84b84392e11da72aa4d0b6f4f0943 you can pass multiple site handles to the site element query param (or multiple site IDs to the siteId param). You can also pass site('*') to query across all sites, or site(['not', 'siteHandle1', 'siteHandle2']) to exclude specific sites.

When you query across multiple sites, any elements that are enabled for multiple sites will be returned multiple times (once per site). This is great for cases when you want to create a language picker and get all of the localized versions of an entry at once. However there are cases where that wouldn鈥檛 be desired, so we鈥檝e also added the unique param (5a280602fc5c366d47702361652505a5f3ad99bb), which can be used in conjunction with site, which will filter out duplicate elements from the results.

{% set entries = craft.entries()
    .section('news')
    .site('*')
    .unique()
    .all() %}

We just tagged 3.2.0-alpha.1. If you鈥檝e already updated a site to the 3.2 branch, please see the updated instructions above to update Craft to the tag, at which point you will be able to continue updating to new 3.2 releases like normal Craft updates.

Hi, would it be possible to give a little more info on how to "create a language picker and get all of the localized versions of an entry at once". This is exactly what I need. I'm using element-api and have an endpoint and need to get the slug of all entries on other sites/locales with the same id to pipe into a language switcher (in a static site generator build). Below I'm attempting get the slug for the same entry on all sites but incorrectly. I'm attempting something like:

$allEntries = $entry->find()->site('*')->slug;

use craft\elements\Entry;
use craft\helpers\UrlHelper;
return [
  'endpoints' => [
        'posts.json' => function() {
            $langHandle = Craft::$app->request->getQueryParam('lang', 'en');
            return [
                'elementType' => Entry::class,
                'criteria' => ['section' => 'page', 'site' => $langHandle],
                'transformer' => function(Entry $entry) {
                    $parent = $entry->getParent();
                    $allEntries = $entry->find()->site('*')->slug;
                    return [
                        'id' => $entry->id,
                        'type' => $entry->type['handle'],
                        'title' => $entry->title,
                        'slug' => $entry->slug,
                        'locale' => $entry->locale,
                        'body' => $entry->body,
                        'level' => $entry->level,
                        'parent' => $parent ? [
                            'title' => $parent->title,
                            'url' => $parent->url,
                        ] : null,
                        'allEntries' => $allEntries
                    ];
                }
            ];
        }
    ]
];

I managed to get the appropriate slug using the following (only selecting one 'site'):

$allEntries = Entry::find()->site('cy')->id($entry->id)->first();

and then:

'locale_slug' => $allEntries->slug

Although I updated to the release/tag stated above the '*' selector for all sites didn't work. It's not a problem for the work i'm currently doing as we only have one other language but will be adding others in the next phase of the project so would be helpful to know why it's not working. I updated composer etc

I'm using element-api and have an endpoint and need to get the slug of all entries on other sites/locales with the same id to pipe into a language switcher (in a static site generator build)

@boxadesign If I鈥檓 understanding you correctly, this is how you鈥檇 do it:

'slugs/<entryId:\d+>.json' => function(int $entryId) {
    $excludeSite = Craft::$app->request->getQueryParam('site');
    return [
        'elementType' => Entry::class,
        'criteria' => [
            'id' => $entryId,
            'site' => $excludeSite ? ['not', $excludeSite] : '*',
        ],
        'transformer' => function(Entry $entry) {
            return [
                'site' => $entry->site->handle,
                'slug' => $entry->slug,
            ];
        }
    ];
},

With that endpoint in place, you could go to /slugs/X.json to get all of the site handle/slug combinations returned, or you could go to /slugs/X.json?site=foo to get all the site handle/slug combinations excluding the site with a handle of foo. (In both cases, X represents the entry ID you want slugs for.)

@brandonkelly Thanks for that it's very helpful. I definitely can't get the '*' to get all sites working though. This is after following your instructions of using the alpha release.

@boxadesign It鈥檚 working on my end. Can you email [email protected] so we can help troubleshoot from there?

@brandonkelly but it is not possible to have relations in the CP for that?
Our problem at the moment is we have a team channel with over 900 entries. and 30 multisites. We have to propagate this team channel over all the sites to select team members. Is there a way how we can handle that much smarter?

@davidhellmann Maybe you鈥檙e looking for #3584? (Also on the list for 3.2.)

This is a helpful addition when working with craft in combination with craftql!
Testing out the beta branch i noticed tho, that this doesn't work on globals.
Is there any plan to add support for that as well?

@seamofreality Global sets support the same multi-site query improvements that other localizable element types get in 3.2.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bitboxfw picture bitboxfw  路  3Comments

richhayler picture richhayler  路  3Comments

angrybrad picture angrybrad  路  3Comments

davist11 picture davist11  路  3Comments

RitterKnightCreative picture RitterKnightCreative  路  3Comments