Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ElementQuery::site/siteId: Allow multiple? #2854

Closed
timkelty opened this issue May 7, 2018 · 22 comments
Closed

ElementQuery::site/siteId: Allow multiple? #2854

timkelty opened this issue May 7, 2018 · 22 comments
Labels
enhancement improvements to existing features site development 👩‍💻 features related to website/API development

Comments

@timkelty
Copy link
Contributor

timkelty commented May 7, 2018

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

@brandonkelly brandonkelly added enhancement improvements to existing features site development 👩‍💻 features related to website/API development labels May 7, 2018
@brandonkelly
Copy link
Member

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

@khalwat
Copy link
Contributor

khalwat commented May 7, 2018

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

@timkelty
Copy link
Contributor Author

timkelty commented May 8, 2018

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)’s part):

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

@brandonkelly
Copy link
Member

Doh, that is a doc bug; fixed for the next release. Just as you couldn’t select elements from more than one locale in Craft 2, you can’t (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.

@bartrylant
Copy link
Contributor

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

@brandonkelly
Copy link
Member

Not currently being developed, no.

@Harry-Harrison
Copy link

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();

@davidhellmann
Copy link
Contributor

Damn, I need this too now :D

@kmgdevelopment
Copy link

I also need this!

@jt-systems
Copy link

jt-systems commented Jan 17, 2019

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 %}

<ul>
{% for entry in multiSiteEntries|supersort('rsortAs', '{{ object.postDate|date("U") }}') %}
 <li>
  <a href="{{ entry.url }}" title="{{entry.title}}">
   {{ entry.title }} | {{ entry.postDate|date('jS F Y')}}
 </a>
 </li>

{% endfor %}
</ul>

@simonleadbetter
Copy link

Yup. I’ve 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?

@brandonkelly
Copy link
Member

brandonkelly commented Mar 11, 2019

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 d017022 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’t be desired, so we’ve also added the unique param (5a28060), 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() %}

@brandonkelly brandonkelly added this to the 3.2 milestone Mar 11, 2019
@brandonkelly
Copy link
Member

We just tagged 3.2.0-alpha.1. If you’ve 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.

@boxadesign
Copy link

boxadesign commented Mar 28, 2019

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
                    ];
                }
            ];
        }
    ]
];

@boxadesign
Copy link

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

@brandonkelly
Copy link
Member

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’m understanding you correctly, this is how you’d 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.)

@boxadesign
Copy link

@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.

@brandonkelly
Copy link
Member

@boxadesign It’s working on my end. Can you email support@craftcms.com so we can help troubleshoot from there?

@davidhellmann
Copy link
Contributor

@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?

@brandonkelly
Copy link
Member

@davidhellmann Maybe you’re looking for #3584? (Also on the list for 3.2.)

@seamofreality
Copy link

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?

@brandonkelly
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement improvements to existing features site development 👩‍💻 features related to website/API development
Projects
None yet
Development

No branches or pull requests