Using cache tag on array outputs nothing Okay. I've figured it out but it left me with the question. After some page refreshes It's got stack back at empty output again.

Bad:

{% cache %}
  {% for item in orders %}
    {# Following variable was the problem #}
    {% set cart = item.cart %}
    {# Loop in cart's table fields: title and quantity #}
    {% for row in cart %}
      {% set data = data|merge([{ title: row.title, quantity: row.quantity }]) %}
    {% endfor %}
  {% endfor %}
{% endcache %}

On second page load it outputs nothing. So I simply call for a check like this.

{% for item in data %}
  {# for example #}{{ item.title }}
{% endfor %}

Good:
This is my current state of the code design. It's a bit extended to the original post. But a problem may be in data merging into an array.

{# Prep array #}
{% set data = [] %}

{% cache %}
  {% for item in orders %}
    {# Loop in cart's table fields: title and quantity #}
    {% for row in item.cart %}
      {% set data = data|merge([{ title: row.title, quantity: row.quantity }]) %}
    {% endfor %}
  {% endfor %}
{% endcache %}

{% set someProduct = 'someProduct' %}
{% set someOrderQuantity = 0 %}
{# Count order's quantity #}
{% for item in data %}
  {% if someProduct = item.title %}
    {% set someOrderQuantity = someOrderQuantity + item.quantity %}
  {% endif %}
{% endfor %}

Setting variable cart to reuse in orders loop and wrap  in cache tags wasn't good idea because it outputted nothing. So I had to take cart variable out. It worked out but I don't know why was setting variable in entries loop for another loop causing that empty output?
I properly don't know how cache tags works?

The {% cache %} tag is only designed to do one thing – to capture output (e.g. HTML) and store that output to the database, so that the next time Craft encounters that particular cache tag, it can serve up the cached output from the database, instead of having to run whatever code is contained inside the {% cache %} tag pair, on every pageload.

In your code, you're attempting to cache a variable (data). That won't work. Essentially, you're just feeding the {% cache %} tag empty output, and the code that actually sets the data variable will only run on the first pageload (or whenever the cache has been cleared out). On subsequent pageloads, Craft won't bother to actually run the code inside the {% cache %} tag, it'll simply print whatever output was stored for that particular cache (which, in this case, is empty). Hence your code breaking.

What you need to do, is to revise your caching strategy so that your business logic (i.e. the part of your code that pulls and operates on data – which is usually the expensive part, resource-wise) and the part that actually prints your data is contained within the same {% cache %} tag pair.

Here's a small example to illustrate good vs. bad use of the {% cache %} tag:

Bad

{% cache %}
    {% set entries = craft.entries.section('news') %}
{% endcache %}

{% for entry in entries %}
    {{ entry.title }}

{% endfor %}

^ Ouch. The variable entries will only be set whenever the cache is empty; on subsequent pageloads, Craft will serve up an empty, cached string instead of actually running the craft.entries query and the for loop will fail because it'll operate on a variable that isn't set.

Good

{% cache %}

    {% set entries = craft.entries.section('news') %}

    {% for entry in entries %}
        {{ entry.title }}

    {% endfor %}

{% endcache %}

^ Awesome. The page will probably be served a bit faster because Craft doesn't have to pull the entries and compile the HTML for every pageload – it'll just spit out the output resulting from the code within the {% cache %} tag.


Getting back after forwarding to another controller function I want to assign a new user to a user group using a variable passed in the frontend registration form. I was going to listen for the OnSaveUser event but I don't think there's a way to get the usergroup from the original post that way(?) so I've instead created a controller that forwards to users/onSaveUser using:

$forward = $this->forward('users/saveUser', false);

The idea being I'll then have access to the post after the user has been created. The problem is everything ends here and doesn't reach the rest of my function. Here's my controller:

<?php
namespace Craft;

class UserGroupController extends BaseController
{

    protected $allowAnonymous = true;

    public function actionSaveUser()
    {
        $this->requirePostRequest();

        $forward = $this->forward('users/saveUser', false);
        throw new Exception('Made It back!'); // never reached
        return TRUE;
    }

}

According to the Yii docs, false should stop the things ending, right?

I have this working in another plugin calling another function:

$forward = $this->forward('commerce/products/saveProduct', false);

and it returns to the original controller fine. So, as Brad comments, the forward is actually working as expected but that particular UsersController->saveUser() function prevents it happening internally.

Within my particular example, I was able to access the original post request and access a userGroupId from the user registration form like so:

    craft()->on('users.onSaveUser', function (Event $event) {
        if ($event->params['isNewUser']) {
            if (isset($_POST['userGroup'])) {

                $userGroupId = $_POST['userGroup'];
                $user = $event->params['user'];

                craft()->userGroup->setUserGroup($user, $userGroupId);

            }
        }
    });


Set multiple entries from different categories merge or join? So I'm using cookies to determine what entries I want to display on some specific pages. I have a lot of different cookies which will change which entries I should be displaying. I currently have around 12 different cookies so would end up with over a thoasad different possiblities. Here is an example if a person has a both the below cookies, they should see entries from all of the below sets so I would need to either merge or join these if that specific cookie was found and the entries for that match were set.

{% if getCookie('cookie1') %}
{% set cookie1Entries = craft.categories.group('resource').slug('slug1') %}
{% set cookie1Entries2 = craft.categories.group('resource').slug('slug2') %}
{% set cookie1Entries3 = craft.categories.group('resource').slug('slug3') %}

{% set entriescookie1 = craft.entries({
    group: 'resource',
    relatedTo: [
        'and',
        { targetElement: cookie1Entries },
        { targetElement: cookie1Entries2 },
        { targetElement: cookie1Entries3 }
    ]
}).limit(3).find() %}
{% endif %}

{% if getCookie('cookie2') %}
{% set cookie2Entries = craft.categories.group('resource').slug('slug4') %}
{% set cookie2Entries2 = craft.categories.group('resource').slug('slug5') %}
{% set cookie2Entries3 = craft.categories.group('resource').slug('slug6') %}

{% set entriescookie2 = craft.entries({
    group: 'resource',
    relatedTo: [
        'and',
        { targetElement: cookie2Entries },
        { targetElement: cookie2Entries2 },
        { targetElement: cookie2Entries3 }
    ]
}).limit(3).find() %}
{% endif %}

What would be the best way to merge all of these at the end of checking for cookies, and also prevent from showing duplicate entries? Not sure how I would go about joining or merging these entries, as I would need to check if entriescookie1 or entriescookie2 was also set.

Use Craft's indexBy and index them by ID. If then you use Twig's merge function the duplicates will be removed and everything will be available in one array.

And may I suggest that if you're going to have a lot of logics in your templates, you'd better move this to a plugin and rewrite it in PHP. Templates should mostly be about templating, not about logics.


Pulling in matrix fields from a Single onto a different page Just wondering how to pull content from a matrix field on my homepage into another .html file. Basically taking the content from a single and placing it into a different template. This is what I have so far:

{% for event in entry.homepage %}
    {% for block in entry.aloneImages %}
        {% for asset in block.image %}

        {% endfor %}
        {{ block.source }}
        {{ block.credit }}
     {% endfor %}
{% endfor %}

You just need to define entry, which can be done with craft.entries the same way you’d fetch any other entry(s).

{% set entry = craft.entries.section('homepage').first() %}

{% for block in entry.aloneImages %}
    {# ... #}
{% endfor %}


Wrong urls printed for small subset using getNext and getPrev I have a related question regarding previous and next throwing a template error for some entries.  This is similar, but different.

A few entries in the same section depict the wrong previous and next ent.   Instead of linking to the previous or next entry they link to the About or 404 page.  It only impacts 2 or 3 out of 100 entries - but always the same 2-3 entries.

There are no templates errors - just the wrong urls being printed for a small subset of the entries.  Here is the code...

`
                {% set prev = entry.getPrev( 'posts' ) %}
                {% set next = entry.getNext( 'posts' ) %}

        {{ entry.postDate | date('F d, Y') }}

`

Thanks

I believe I already answered this in the OP's other, similar question, but:

You shouldn't pass a string value (i.e. 'posts') to getNext() and getPrev() – those methods expect either an ElementCriteriaModel instance, or an object with attributes representing a query (i.e. something like { section: 'posts' }.

What actually happens when you call entry.getPrev('posts'), is that Craft will just ignore the string value (I'm guessing 'posts' is a section handle, but there's no way for Craft to know that), and you're probably going to get an EntryModel back, but it won't reliably be from the same section as your current entry – it can be an entry from any section in your system, which is probably where your problem is.

So here's how your code should probably look:

{% set criteria = { section: 'posts' } %}
{% set prev = entry.getPrev(criteria) %}
{% set next = entry.getNext(criteria) %}

You can also include other attributes, like for example order (otherwise, default attributes will apply, such as postDate desc for a Channel section, or structure for a Structure):

{% set criteria = { section: 'posts', order: 'title asc' } %}
{% set prev = entry.getPrev(criteria) %}
...

...and of course, you can make the criteria dynamic:

{% set criteria = { section: entry.section } %}
{% set prev = entry.getPrev(criteria) %}
...

Finally, make sure to always wrap the returned values from getNext() and getPrev() in a conditional – you can't always be sure that there'll be a valid "next" or "previous" element for any given entry, so this avoids template errors:

{% set prev = entry.getPrev(criteria) %}
...
{% if prev %}
    {{ prev.title }}
{% endif %}

Of course, it's possible to declare a fallback value whenever there isn't a next or previous element:

{% set next = entry.getNext(criteria) ?? craft.entries(criteria).first() %}


Specify which template/layout to use based on URI segment I have 3 level structure for my entries with separate template for each level.

/artists/artist-name/ uses artists/_types/artists.html template

/artists/artist-name/section-name/ uses artists/_types/section.html template

/artists/artist-name/section-name/project-name/ uses artists/_types/project.html template

What I need to do is to specify additional layout for level 3 entry so depending on URI I can display one or another.

This is the additional URI I want to use:
/artists/artist-name/section-name/project-name/thumb and I want to load the artists/_types/thumb.html template

I'm not completely sure about what you're asking, but you might want to look in to craft.request:
 https://craftcms.com/docs/templating/craft.request

In your structure template, you can check your URL segments, and use twig include to load the desired template, based on those segments.

Another related feature is dynamic routing:
https://craftcms.com/docs/routing#dynamic-routes

You could use this to route a URL to a different template.


Search against a checkboxes field's options I have a relatively simple structure for products that I am building an advanced search for. I access the products using craft.entries.section('products'), but these products have fields with options for those fields within them.

So my question is, how do I access these? This is my attempt so far.

        {% set parameters=
            {
                section: 'products',
                limit: '1'
            }
        %}

        {% set query = craft.request.getParam('entry') %}

        {% if query %}
            {% set entries = craft.entries(parameters).relatedTo(query).order('score') %}
        {% else %}
            {% set entries = craft.entries(parameters) %}
        {% endif %}

I know this is incorrect, because I need to look at an object within an object within products and check a field belonging it. For instance, if the product was a phone, it would have something like "mobilityOptions" and under that would be "Cordless", "Desk", and "Wall".

My search form has got those mobility options through a for option in products loop of the section, followed by a for block in option.mobilityOptions.options that then makes checkboxes for each option, but to use it with the ".relatedTo" functionality I don't think it would work.

Checkboxes / multiselect fields store to the database in a very query unfriendly way.
That's why I would really recommend to use categories, tags, lightswitches instead, if you intend to
query against these fields' values.

Here's what you'd have to do to search for entries with selected checkboxes field types' options. You basically have to get all relevant entries first,
go through them to check each checkbox option and store the element ID in case the entry is valid. Using this approach you end
up with an array of IDs which you can now query against.

{% set entries = craft.entries.section('news') %}

{% set query = craft.request.getQuery() %}

{% if query.mobilityOptions is defined %}
    {% set ids = [] %}

    {% for entry in entries %}
        {% set valid = true %}

        {% for option in query.mobilityOptions %}
            {% if not entry.mobilityOptions.contains(option) %}
                {% set valid = false %}
            {% endif %}
        {% endfor %}

        {% if valid %}
            {% set ids = ids|merge([entry.id]) %}
        {% endif %}
    {% endfor %}

    {% set entries = craft.entries.id(ids) %}
{% endif %}


What are the differences between an elementCriteriaModel and fetched elements? I am a little confused about the differences between an elementCriteriaModel and fetched elements.

Why do I sometimes have to fetch elements with first(), last(), find():

{% set entries = craft.entries.section('cocktails').find() %}

and in other cases the elementCriteriaModel is enough:

{% set params = craft.entries.section('cocktails') %}
{% set prev   = entry.getPrev(params) %}

The ElementCriteriaModel and the individual element models (like the EntryModel) can be thought of as two different points of time in retrieving data from your database. Essentially, you'll use an ElementCriteriaModel before the database is queried and the element models afterwards.

  Why do I sometimes have to fetch elements with .first(), .last(), .find()…

The ElementCriteriaModel does what it says on the tin — it specifies criteria to find elements. When you call .find(), .first() or .last(), the model sends itself off to the ElementsService which will return element models, based on your criteria.

  …and in other cases the ElementCriteriaModel is enough?

Internally, Craft is using ElementCriteriaModels to work things out. For pagination or the .getPrev() example, Craft is finding elements using the same criteria, but it's using it in a different way to .find() or similar methods.

You can omit the .find() method and treat the ElementCriteriaModel as an array, but it depends on your scenario. As was touched on here, there are performance impacts to consider.


Can I send a status message from an external (standalone) PHP script to the Craft engine? Note: this question pertains to Craft 2 Pro. Craft 3 is not currently an option.

What I need to do

I have some stand-alone utilities that don't need to exist within the Craft framework, so I would really rather not have to go down the road of learning how to develop Craft extensions.

The trick is that these stand-alone utilities do their thing, and then redirect the user to various Craft entries, and I need some way to send arbitrary status messages from the standalone utilities that can then be displayed by the Craft templates.

Example Scenario

We are using a external utility for online fundraising. The user starts on a page that exists as a Craft entry ('Donate'), proceeds to the external utility's web space, and is sent back to the Craft site.

If something goes wrong in the transaction, I need to be able to display any number of error messages to the user, on the original Craft entry, allowing the user to try the donation again. I don't want to create multiple copies of the original Craft entry, one for each possible error message. That seems like an unreasonable duplication of effort and data, and a nightmare in the making if any changes need to be made to what the user sees.

What I hoped would be possible

The external utilities exist on the same domain, so I hoped I might be able to use PHP's $_SESSION variable to set the status message, and then have the Twig template display the message.

Based on what I found, it seems that Twig (at least in Craft 2) doesn't have access to $_SESSION without editing the Craft application code to create a new global variable corresponding to $_SESSION. I want to avoid editing Craft application code to ensure that nothing gets clobbered by updates to Craft.

What to do?

How can I send arbitrary external messages to a Craft entry so that they can be displayed by the Twig template? I don't think I know Craft well enough to know all the possibilities, and so I am not even sure how to search well for what other people have done.

I hope that writing my own extension isn't the answer to this because I haven't figured out how to do that, yet!

Update!

I went ahead with what was recommended in the "accepted answer" below, and it has worked. Here are the steps that I took:

Add a custom route in config/routes.php that looks like this:
'(?:[-a-zA-Z0-9@:%_\+.~#?&\/=]+\/)+error/(\d+)' => '_entry',
This matches any URI that ends with /error/ and a number, and will capture the number as a variable. For an explanation of the regex, here it is on regex101.com.
Note: The version on regex101.com needs one of the forward slashes escaped, but that slash in config/routes.php should not be escaped.
Create a macro that will look up and return the correct error message by using a switch statement on the error number, which will be captured in the matches variable. The macro looks like this:

{% macro show_error(error_id) %}
  {% switch error_id %}
    {% case 10072 %} Error: bad transaction, please try again
    {% default %} Error: error message not found
  {% endswitch %}
{% endmacro %}

Add code at the beginning of _entry to detect if the matches variable exists, and then:

look up what entry would have been loaded by the CraftCMS engine, if this weren't a custom route: {% set entry = craft.entries.uri(matches[0]).first() %}
save the error message to a variable, using the previously defined macro: {% set error_message = show_error(matches[1]) %}

Display the error message at the appropriate place in the template:

{% if error_message is defined and error_message|length %}

{{ error_message }}

{% endif %}`

Result: I can append /error/123 to any entry's URI, and the _entry template will be able to integrate the externally-chosen error message in an internal template.

Probably the easiest way to do this would be to add another segment to your URI.
i.e /bla/bla/bla/message-1
Then in your template, read the segment and display the appropriate message.
You could do it all in the template with a switch or conditional or the segment could be the entry slug for the message and you can pull from Craft entries.


How can I include a Users picker in a custom field type? I would like to include a Users picker in a custom field type. Not sure if this is possible and practical.

Do you know how I can accomplish this, or can you point me in the right direction for self-education?

I see there is no users macro in /app/templates/_includes/forms.html, /app/fieldtypes/UsersFieldType.php has almost nothing in it, and I don't know enough about the system yet to know where else to look for ideas.

I don't know your requirements, but you could build a "Users Picker" with the Dynamic Fields plugin. You would basically need to do the same in its settings to build your select options as you'd do in a custom plugin.

You get your users with craft.users and then build an array with "label" and "value" keys using the merge filter.

Pass that array into the Dynamic Fields' settings or build the select options with the forms.selectField macro in your own plugin.

{% set users = craft.users.group('clients') %}

{% set userOptions = [] %}
{% for user in users %}
    {% set userOptions = userOptions|merge([{ label: user.friendlyName, value: user.id }]) %}
{% endfor %}

{{ forms.selectField({
    label: 'User'|t,
    id: id,
    name: name,
    options: userOptions,
    value: value ? value
}) }}


400 error on Craft 3 installation When I try to install Craft CMS 3 by going to /admin the site redirects me to /index.php?p=admin/install where the Begin button shows. Clicking next send me to the screen to create an admin user.

When I enter all the information I see an ajax call going to http://domain.com/index.php?p=admin/actions/install/validate-account but that responds with 400 Error.

Any idea whats wrong? I have .htaccess file in place, so not sure why I see the index.php part in the url. Any help would be appreciated.
Edit: in the error log file I see those three lines

[-][-][error][yii\i18n\PhpMessageSource::loadMessages] The message file for category 'yii' does not exist: /home/rrznjzkn/public_html/craft3/craft/app/vendor/yiisoft/yii2/messages/en-US/yii.php Fallback file does not exist as well: /home/rrznjzkn/public_html/craft3/craft/app/vendor/yiisoft/yii2/messages/en/yii.php

[-][-][warning][craft\app\web\ErrorHandler::handleException] 400 - Unable to verify your data submission.

[-][-][error][yii\web\HttpException:400] exception 'yii\web\BadRequestHttpException' with message 'Unable to verify your data submission.' in /home/rrznjzkn/public_html/craft3/craft/app/vendor/yiisoft/yii2/web/Controller.php:110

You're getting that error because CSRF validation is failing.  Whether that's a bug in the Dev Preview of Craft 3 or something environmental on your end is hard to tell with the given information.  I can't reproduce it locally, though.


How can I set the "activateAccountSuccessPath" for users with Control Panel access? I have a site that allows public registration, and I want to be able to control where users are redirected to after activating their account. However the default user group has access to the Control Panel, so as explained in “activateAccountSuccessPath doesn't work as expected”, users are taken to the Control Panel login page after activation, rather than the path defined by the activateAccountSuccessPath config setting.

Is there any way to change that behavior, so even users with access to the Control Panel will get redirected to the activateAccountSuccessPath after account activation?

You can write a plugin that listens to the users.onActivateUser event, and manually redirects the browser to your desired page.

Just add this code to your plugin’s primary class:

public function init()
{
    craft()->on('users.activateUser', array($this, 'onActivateUser'));
}

public function onActivateUser(Event $event)
{
    /* @var UserModel $user */
    $user = $event->params['user'];

    // Are they in the default group?
    // (Indicates this was probably public registration)
    if ($user->isInGroup('myDefaultGroup'))
    {
        // Redirect them to the activateAccountSuccessPath
        $activateAccountSuccessPath = craft()->config->getLocalized('activateAccountSuccessPath');
        $url = UrlHelper::getSiteUrl($activateAccountSuccessPath);
        craft()->request->redirect($url);
    }
}


Looping Related Entries with a Super Table I need to loop related entries.

I am using a SuperTable field with the handle "related" with an entry field with the handle "contentEntry" and two text fields for alternative text and descriptions.

The "contentEntry" field is limited to two entries being selected. The issue I'm having is that the following code repeats the first entry twice and doesn't loop and display the second. What am I doing wrong?

{# Related Entry #}

{% set relatedEntries = block.related.all %}
{% set relatedEntry = block.related.contentEntry.all() %}
{% set relatedAltDesc = block.related.altDescription %}
{% set relatedAltTitle = block.related.altTitle %}

{% if relatedEntries %}

            {% for entries in relatedEntries %}

              {% for rel in relatedEntry %}

                {% if relatedAltTitle %}
                  {{ relatedAltTitle }}
                {% else %}
                  {{ rel.title }}
                {% endif %}

                {% if relatedAltDesc %}
                  {{ relatedAltDesc }}
                {% else %}
                  {{ rel.previewDescription }}
                {% endif %}

              {% endfor %}

            {% endfor %}

{% endif %}

The issue is: you don't really loop your entries at all.

{# get all super table rows #}
{% set superTableRows = block.related.all %}

{% if superTableRows is not empty %}

            {# loop all of your super tables #}
            {% for row in superTableRows %}

                {# loop all contentEntries in each row #}
                {% for entry in row.contentEntry.all() %}

                    {#
                        display the alternative title set in your supertablerow
                        and if thats not set display the entries title
                    #}
                    {{ row.altTitle?? entry.title }}

                    {# same as above #}
                    {{ row.altDescription ?? entry.previewDescription }}

                {% endfor %}
            {% endfor %}

{% endif %}

block.related is an Object of type ElementQuery. block.related.altDescription returns the criteria of this Query, this is mostly empty since no criteria is set by default.


Merging Assets Fields I've got this working but it seems terribly verbose to do something, that in my opinion should be easier.

Is there a better way to get the assets from 2 different fields, merge them and output them in a random order?

{% set clientLogos = logos.clientLogos.all() %}
{% set agencyLogos = logos.agencyLogos.all() %}

{# set empty array #}
{% set logoIds = [] %}

{# add logos from clients #}
{% for logo in clientLogos %}
  {% set logoIds = logoIds|merge([logo.id]) %}
{% endfor %}

{#  add logos from agencies #}
{% for logo in agencyLogos %}
  {% set logoIds = logoIds|merge([logo.id]) %}
{% endfor %}

{# looped through merged logos #}
{% for logo in craft.assets().volume('uploads').kind('image').id(logoIds).orderBy('RAND()') %}
  <img src="{{ logo.url(croppedlogo) }}" alt="{{ logo.title }}" class="m-4"/>
{% endfor %}

From Twig, this seems like a decent solution.

Could possibly be streamlined a bit if you wanted to do it from PHP, but as long as you're not dealing with large amounts of images, it's probably not worth it.


Users not fully deleting from the database From the admin, if I delete a user they disappear from the admin list but not from the users database table.
Looking through the code I can see UsersService.php has a function deleteUser which calls the deleteElementById from ElementsService.php - but it does not seem to actually remove the user from the main users table at all.
The version of craft in use has recently been updated to 2.9.2 following the Craft CMS guide and I've read there maybe Foreign Key issues in the database, but the fixes (plugins) seem to only be for Craft 3, and we are unable to upgrade at this time.
Also looked at > Users not being deleted from the craft_users table?
But as this did not have a full answer and is not a fix for the CMS side of things it's not viable.
How can I determine 1) where the issue is, and 2) how to resolve it?

It definitely does sound like a missing foreign key constraint issue.
The Craft 2 version of the Fix FKs plugin for Craft 3 still lives here: https://github.com/pixelandtonic/craft-fixfks
I'd give that a whirl.


Anyway to programmatically add default fields to a layout? So I've been building my plugin to import product listings directly from Salesforce. So far I've been making progress. However, I've approached another roadblock and I was wondering how I would approach this issue. I want to write some code to add default fields into my product layout (e.g.- Body richtext, Image Asset field),

Someone showed me an example of this function that adds default content for a ProductType

/**
 * @throws \Craft\Exception
 * @throws \Exception
 */
private function defaultProductTypes()
{
    $productType = new Commerce_ProductTypeModel;
    $productType->name = 'Clothing';
    $productType->handle = 'clothing';
    $productType->hasDimensions = true;
    $productType->hasUrls = true;
    $productType->hasVariants = false;
    $productType->hasVariantTitleField = false;
    $productType->titleFormat = "{product.title}";
    $productType->template = 'shop/products/_product';

    $fieldLayout = FieldLayoutModel::populateModel(['type' => 'Commerce_Product']);
    \Craft\craft()->fields->saveLayout($fieldLayout);
    $productType->asa('productFieldLayout')->setFieldLayout($fieldLayout);

    $variantFieldLayout = FieldLayoutModel::populateModel(['type' => 'Commerce_Variant']);
    \Craft\craft()->fields->saveLayout($variantFieldLayout);
    $productType->asa('variantFieldLayout')->setFieldLayout($variantFieldLayout);

    \Craft\craft()->commerce_productTypes->saveProductType($productType);

    $productTypeLocales = \Craft\craft()->i18n->getSiteLocaleIds();

    foreach ($productTypeLocales as $locale) {
        \Craft\craft()->db->createCommand()->insert('commerce_producttypes_i18n', [
            'productTypeId' => $productType->id,
            'locale' => $locale,
            'urlFormat' => 'shop/products/{slug}'
        ]);
    }
}

I've tried to read through to see where I could include extra fields, but I'm a bit lost on where to start. Can anyone provide some insight or approach to this?

After hours of perusing, I managed to find a working solution to this issue.  I threw this implementation from InstallService located in craft/app/services.

I realized that CommerceProduct and Variant Field Layouts were set into the Commerce Product Type Model via setFieldLayout() function, therefore all I would need to do is create a new Field Layout with the fields that I required and set them into defaultProductTypes().

    //Write this code in between CommerceProductFieldLayout and Variant type field Layout

    //Any custom fields for your products go here.
    //Format: $yourFieldName = FieldsVariable::getFieldByHandle("Your field's
handle");
    $bodyField = FieldsVariable::getFieldByHandle('body');
    $tagsField= FieldsVariable::getFieldByHandle('tags');

    //Assemble a new layout that contains the fields you need.
    $infoLayout = craft()->fields->assembleLayout(
        array(
            'Content' => array($bodyField->id, $tagsField->id) //Add fields into this array to display your plugins. (e.g. - $YourFieldVariable->id)
        ),
        array($bodyField->id) //Add any required fields here.
    );

    // Save Layout into specified product type.
    $productType->setFieldLayout($infoLayout);


Entries object getting reset after a relatedTo query This is from a template that worked in Craft 2, but doesn't in Craft 3.

{% set entries = craft.entries.type('publications').orderBy("postDate desc") %}

entries.count() outputs 80

{% for cat in allCats %}
    {% set catEntries = entries.relatedTo(cat) %}
    {% if catEntries | length or cat.slug=="all" or cat.slug=="toutes" %}

{{ cat.title }}

    {% endif %}
{% endfor %}

This iterates through all categories with correct entries associated

After that, entries.count() outputs 0

Looks like entries is getting set to the last value instead of just passing the resulting object to the variable.  This is not how it worked before.  Is there a way to reset this ?

Based on the bug report found here (https://github.com/craftcms/cms/issues/2160), I am now creating a variable that contains all of the items prior to filtering it.

{% set entries = craft.entries.type('publications').orderBy("postDate desc") %}
{% set allEntries = entries.all() %}

I can then use entries to filter through the available categories and then use allEntries to loop through all of them.

I understand I could also have used clone() to get a copy to work from in my category loop.

{% set catEntries = clone(entries).relatedTo(cat) %}


Fatal PHP error upon installation I attempted to install the newest version of Craft and received this error in my logs:

PHP Fatal error:  Maximum execution time of 30 seconds exceeded in C:\xampp\htdocs\craft-cms\vendor\yiisoft\yii2\db\Command.php on line 1292

I'm unsure of the cause of this error, but the the lines around 1292 in the command.php file seem to be an exception handler for database transactions.

I don't achieve a different result after lengthening the execution time. I've tried setting very lenient firewalls and administrator rights. I've also tried uninstalling and reinstalling all of the files. I still receive the same error every time.

Any ideas to fix this issue? I haven't found another post about this issue online, so any help would be very much appreciated.

For anyone with the same issue- I was able to fix the issue by setting the execution time as infinite and then waiting for an hour.


Limiting the Upload Location for assets in a Matrix Block In the Dashboard, while editing the settings of a Matrix Block — the instructions for setting Upload Location for an Assets field says Note that the subfolder path can contain variables like {owner.slug} or {owner.author.username}.

To what does owner refer?

Ideally I want to store these assets in Site Assets/pages/{slug of the page this block is on}  — is owner the page name or the username or what?

owner is the element which contains your Matrix field.

For example, if you attach a Matrix field to a Section, the owner would be the specific Entry in that Section.

{owner.slug} - The slug of your entry.
{owner.author.username} - The username of the person who created the entry.


Outputting Year of Entry Group alongside Month I have this piece of code which orders my entries by month, which is working great. I only have two issues which I can't work out. How would I display the year of the month beside the month title eg:

{{ month }} > {{ year }}

As the list could run over a number of years it would be good to distinguish this. The next part is I can't seem to convert the month from a number ie: 8 to the word ie. "august". When I filter it, it just returns "Jan".

{% set allEntries = craft.entries.section('featureArticles').find() %}
{% set allEntriesByMonth = allEntries|group('postDate.month') %}

{% for month, entriesInMonth in allEntriesByMonth %}

{{ month }}

        {% for entry in entriesInMonth %}

{{ entry.title }}

        {% endfor %}

{% endfor %}

You're close!

We've got an example of how to do this here: http://buildwithcraft.com/help/entry-archive#grouping-by-year-and-then-month

The trick is to group by year first, then by month.  Using the 'F' token is how to tell PHP to display the name of the month.


How to get a list of fields on an entry How do you get a list of the fields that are on an entry?

I tried {{ dump(entry.subentry) }} but that isn't any help ...

I think you can use this:

{% for field in entry.subentry[0].getFieldLayout().getFields() %}
    {{ field.getField().name }} {# or .handle #}
{% endfor %}

You might try a great little plugin called inspector that prints out all of the attributes and methods of an object using either filter {{ entry.subentry|inspect }} or method {{ inspect(entry.subentry) }} syntax. Its output is a little cleaner than dump, and nice for following the rabbit-hole.


Is it possible to create a new multisite site via a Craft Plugin/Module? Does Craft provide a way to programmatically add a new multisite site through a plugin or module?

You have access to the same APIs that Craft itself has; so sure, you could programmatically create new sites via a plugin or module.


Composer issue updating sprout plugins I cant update my sprout plugins due to a dependency / version issue with sprout base. Ive tried updating via composer - but I think I need to edit my composer file to work around the sprout base dependency issue - not certain of what that edit should be - any help appreciated.

Your requirements could not be resolved to an installable set of packages.

Problem 1
- barrelstrength/sprout-email 4.0.0-beta.15 requires barrelstrength/sprout-base ^4.0.8 -> satisfiable by barrelstrength/sprout-base[4.0.8].
- barrelstrength/sprout-email 4.0.0-beta.15 requires barrelstrength/sprout-base ^4.0.8 -> satisfiable by barrelstrength/sprout-base[4.0.8].
- barrelstrength/sprout-email 4.0.0-beta.15 requires barrelstrength/sprout-base ^4.0.8 -> satisfiable by barrelstrength/sprout-base[4.0.8].
- Can only install one of: barrelstrength/sprout-base[5.0.0, 4.0.8].
- Can only install one of: barrelstrength/sprout-base[4.0.8, 5.0.0].
- Can only install one of: barrelstrength/sprout-base[5.0.0, 4.0.8].
- barrelstrength/sprout-forms 3.0.0-beta.57 requires barrelstrength/sprout-base ^5.0.0 -> satisfiable by barrelstrength/sprout-base[5.0.0].
- Installation request for barrelstrength/sprout-forms 3.0.0-beta.57 -> satisfiable by barrelstrength/sprout-forms[3.0.0-beta.57].
- Installation request for barrelstrength/sprout-email 4.0.0-beta.15 -> satisfiable by barrelstrength/sprout-email[4.0.0-beta.15].

You've identified the general problem. The easiest path around this issue is to upgrade to the latest of everything all at once. composer update alone will not upgrade to the latest major release, only to the latest releases defined in your composer.json. To make sure you are upgrading to the latest of everything:

Delete your composer.lock file (might not be necessary but I find it helps get around a few potential support scenarios to take this step)
Review each package in the require array of your composer.json
Update each package to the latest version release.
Once all packages are defined with their latest versions, run composer update

Regarding version constraints in your composer.json file, you'll probably want to require an explicit version of Craft and us the Caret Version Range for plugins:

{
  ...
  "require": {
    "craftcms/cms": "3.1.23",
    "vlucas/phpdotenv": "^2.4.0",
    "barrelstrength/sprout-forms": "^3.0.0-beta.57",
    "barrelstrength/sprout-email": "^4.0.0-beta.18",
    ...
  }
  ...
}

By updating to the latest versions of everything all at once, you'll ensure that the major releases of supporting modules that may be used by more than one plugin are updated and resolved together.

If you continue to run into issues you may want to try to run remove commands first (i.e. `composer remove barrelstrength/sprout-forms barrelstrength/sprout-email) or remove your entire vendor folder and go through the steps above again.

You should not need to require barrelstrength/sprout-base. Once you get the primary list if required packages in your composer.json file sorted out, the correct dependency will get resolved.


Partial Order Fulfillment Is it possible to fulfil orders partially? For instance when only two of four products are in stock, would it be possible to ship the two items immediately?

There is nothing stopping you from shipping what you want whenever, but there is no way to mark a line item as shipped. You would need to have custom fields on your order to track partial shipments, and if you wanted, a 'partially shipped' order status to be able to easily see partially shipped orders in the order index page.


Form fields not visible after creating content fields using A&M Forms plugin I'm new to Craft 2, and I created about 20 new form fields in the Settings>Content>Fields section.  I created a new group for them to go in.

Then, I went to create a new Form, but I cannot see those new form fields.

Is there a way to see them?  Thanks

As noted in the documentation for AM Forms the fields that are created for forms

  …are stored in a different context / scope than Craft's fields.

and that;

  …they won't be shown in Craft's field list.

I assume that the reverse is then also true, so fields created in Craft will not be visible in the plugin.


Multiple Photos/Assets in one upload entry I'm having a bit of a problem with using craft currently. My site has a gallery section where I would like to mass-upload image files to an entry which is in the area:

admin/entries/photos

However, when I upload the files in the following fashion, by selecting all of the images, uploading them and then inserting them into the photo entry, the desired effect is not found.

I upload all of the images and select them in the interface
I then am met with this screen, with all of the images I selected in one section.
However, only the first image from the upload stack is shown on the live page. (The last screenshot is confusing, I have uploaded those photos one by one, the photos I tried to mass up load have not worked.)

I hope this is understandable, I do not want to upload 100++ images one by one when I only need the simple functionality of a gallery. Please feel free to ask me questions.

Forget about the front-end for a moment... That would be a separate, unrelated issue.

When you mass-upload, do all files appear on the back-end (screenshot #2)? And then when you hit "Save", are all of those assets saved?

If the answer to either of those questions is no, then you should file a bug report. Use the "Get Help" widget on your Dashboard, and be sure to include your logs, database export, and files.

If the answer to both of those questions is yes, then the problem is not with your file upload. The problem is somewhere in your template. In that case, please post a new question to ask why your template isn't showing all of your uploaded images.


P&T Contacform plugin: what's the use of the errors if fields are not required? In the Contactform plugin page there's a code example that's confusing me.

Only the email and message are required in the code, but the fromName field does have an error syntax:

<input id="fromName" type="text" name="fromName" value="{% if message is defined %}{{ message.fromName }}{% endif %}">
{{ message is defined and message ? errorList(message.getErrors('fromName')) }}

What's the use of the getErrors here? I've tried using rare characters, but no errors?

In the ContactFormModel you can manage the attribute 'required' => true.

The example just show how to use the getErrors for each field.

You can delete the ones you dont need.


Unexpected token "punctuation" of value "?" We’re running into issues with the null coalescence operator in the latest version of Craft (2.6.2989).

Does anyone know why this might be happening?

The issue was a plugin’s composer.json that was loading an older version of Twig as a nested dependancy, which was overriding Craft’s Twig.

Running a composer update for that plugin rectified the issue.


Controlling where user is sent if clicking old or expired email activation link When a user visits on an old or expired email activation link, how do we control which page is loaded? In my case it's rendering a template named error.twig, but I'm not sure how that's set or where to change it.

It looks like the config setting activateAccountFailurePath is what you are looking for.


Entries Related by Category with Filler Entries After At the bottom of an entry I want to display links for eight other posts. To make this useful, what I'd like to do is:

start with entries that are related to the post currently being viewed by category (generally there are 4-5 of these)
then fill in the remain slots with the other posts (not related by category), newest first

Is there a simple-ish way to do this? I started down the path of separate queries, counting, and merging results but something about that feels more complicated that it should be. I'm wondering if this can be achieved via ordering or grouping?

Thanks in advance.

This is probably pretty much the same code you already have, but I can't think of a more elegant solution:

{% set relatedEntriesIds = craft.entries.section('news').relatedTo(entry).limit(8).ids() %}

{% if relatedEntriesIds|length < 8 %}
    {% set moreRelatedEntriesIds = craft.entries.section('news').limit(8 - relatedEntries|length).ids() %}
    {% set relatedEntriesIds = relatedEntriesIds|merge([moreRelatedEntriesIds]) %}
{% endif %}

{% set relatedEntries = craft.entries.id(relatedEntriesIds).fixedOrder(true) %}


System Status flag in template context does anyone know if there is a flag, accessible at template level, that indicates whether the "System Status" lightswitch in Settings -> General menu is on or off? (I'm thinking something similar to craft.config.devMode)

I tried craft.config.isSystemOn but it did not work since the setting is not defined in the general.php config but rather via the user interface.

Just to give some context, I'd like to add extra items to the template for logged in admin users to see on the website only when system status is set to off (like a warning bar).

Thanks in advance!
Daniel

You could use craft.app.isSystemOn. This will return 1 if the System Status is "on" in Settings -> General in the control panel. It will also respect the value of the isSystemOn config setting if you're setting it in general.php


Force Anchor ID into page URL I'm trying to add tabs into my Craft Site and have just noticed that when you add an anchor point, the anchor ID doesn't actually show up in the URL as it would a normal site, however I need this anchor to be able to target certain tabs in the menu.

Is there a way or plugin that forces to anchor to be added to the url?

Example:

mysite.com/tabs#anchor1

mysite.com/tabs#anchor2

mysite.com/tabs#anchor3

Craft CMS does absolutely nothing to your front end code, and there's literally nothing in Craft that would make anchor links – i.e. Click – in your front end markup behave in any other way than normal.

The error is probably somewhere in your front end code (either in your Twig/markup or possibly in your JavaScript).


If featured or latest entry I'm trying to include different markup if a blog post is featured.
This is done via a Lightswitch. If there's is no featured entry then the latest entry uses this different markup instead.
The rest of the entries follow this but need to exclude showing the featured entry if it is shown above.

This is something like what I'm trying to achieve

{% set featured = craft.entries.section('blog').featured(1).limit(1) %}
{% paginate craft.entries.section('blog').limit(12) as entriesOnPage %}
{% for entry in entriesOnPage %}
    {% if entry is featured or loop.first and not featured|length %}

       [markup A]

    {% else %}

       [markup B excluding featured item]

    {% endif %}
{% endfor %}

You could try this

{% set featured = craft.entries.section('blog').featured(1).count() %}
{% paginate craft.entries.section('blog').limit(12) as entriesOnPage %}
{% for index, entry in entriesOnPage %}
    {# entry is featured or it's the first entry and there are no other
    featured entries #}
    {% if entry.featured or (index == 0 and featured == 0) %}
        [markup A]
    {% else %}
        [markup B excluding featured item]
    {% endif %}
{% endfor %}


How to suppress display of expired entries in Solspace month calendar? I'm trying to suppress display of expired events (events that occurred in the past) on the Solspace month calendar. I'm using the out-of-the-box templates included with Solspace Calendar for Craft. Initially, I thought that there would be a start and end range for the calendar.month function:

{% set month = craft.calendar.month({
    date: targetDate,
    calendar: calendarHandle,
}) %}

But looking at the documentation, there are no parameters to pass. (https://solspace.com/craft/calendar/docs/calendar.month/)

So, I looked at what is happening, and it seems that the start and end should be set on the event object (https://solspace.com/craft/calendar/docs/event/)

In the month template, the event object is called here:

{% for event in day.events %}
    {% include "cal/layouts/_event.html" %}
{% endfor %}

And this is what is returned:

{% set classes = "" %}
{% set classes = classes ~ (event.allDay ? " all_day") %}

{% if event.multiDay %}
    {% set classes = classes ~ " multi_day" %}
    {% set classes = classes ~ (day.containsDate(event.startDate) ? " first_day") %}
    {% set classes = classes ~ (day.containsDate(event.endDate) ? " last_day") %}
{% endif %}

{% set style = "" %}
    {% if event.multiDay or event.allDay %}
        {% set style %}
    style="background-color: {{ event.calendar.lighterColor }} !important;
        border-color: {{ event.calendar.color }} !important;"
    {% endset %}
{% endif %}

{% if event.startDate.format('U') > now|date('U') %}

        {% if event.multiDay %}

                {% autoescape false %}
                    {{ day.containsDate(event.startDate) or day.containsDate(event.endDate) ? event.title : "&nbsp;"|raw }}
                {% endautoescape %}

        {% else %}
            {% if not event.allDay %}

            {% endif %}

                    {{ event.title }}

        {% endif %}

    {% include "cal/layouts/_event_popover.html" %}

{% endif %}

I assume this is where I would set a date range, but I'm not really clear where I would set it?

UPDATE: I managed to get this working by wrapping the event output in a conditional:

{% if event.startDate.format('U') > now|date('U') %}

I added this to the code above.

The solution is to wrap the event output in a conditional that compares the event startdate to now. I've updated the question to reflect this.


New section/entry can't go live I've created a new section, with one entry. When I want to live preview the entry, the live preview button isn't there.

I've read this is because my new section doesn't have an active template? How do I resolve this?

I'm very new to this!

Have you already made up an html template for this section?

If so you should check if you assigned it right.
Go to: Settings>Sections>(your new section you want to assign) and check the entry template. It should be something like (folder you made the html template in if there is any)/(name of file)

Without any written HTML its not possible to have a live preview as it doesn't output anything.

If thats alright could you post some pictures of your Folders and your back office?


Creating a related content subpage to a channel entry? For a project I'm working on I have set up several channels that are related to each other. What the setup comes down to is the following:

Channels:

Projects
|- News relations (news is a channel and news entries can be related to a project entry)
|- Gallery relations (gallery is a channel and galleries can be related to a project entry)
|- ... and more related content ...

Now I can visit the projects overview on the /projects path and a project entry on the /projects/{slug} path. The project entry page shows a short overview of the related content (i.e. 2 or 3 of the related items for the various channels), but will also contain a link to the full listing of all news items/galleries/etc of the requested projected.

Preferably I would have a url for these subpages to the project on a path like /projects/{slug}/news and /projects/{slug}/gallery.

How would I set this up? I've been looking around, but can't find any solution to this.

Two ways that this can be done is A) use a structure, or B) use custom routes.

Using a Structure with different entry types defined for the various sub-page types would probably be the most straightforward and flexible. The base of the structure would be projects (but could just as easily start above projects, if you also wanted to use it to generate your site nav). Your structure might look something like this:

/project1 (entry type: project)
   /news (entry type: projectNews)
   /gallery (entry type: projectGallery)
/project 2
   /...

In your projectNews template, you can access the 'project' entry through entry.parent and can retrieve the news related to the project using relatedTo():

{% set project = entry.parent %}
{% set projectNews = craft.entries.section('news').relatedTo(project) %}
{% for entry in projectNews %}

        {{ entry.title }}
        ...

{% endfor %}

The advantage of the structure is that: you can selectively add these pages to projects on a case by case basis; you can add additional content to the news or gallery pages as needed, in addition to the related content; and that it automatically generates the uri that your looking for.

The other option would be to create custom routes, using the project slug as one of the uri segments (i.e. projects/{slug}/news). The variable slug will be accessible in the templates and can be used to fetch the project using an Element Criteria Model, and the related content via relatedTo():

{% set project = craft.entries.section('projects').slug(slug) %}
{% set projectNews = craft.entries.section('news').relatedTo(entry) %}
{% for entry in projectNews %}

        {{ entry.title }}
        ...

{% endfor %}

The advantage here is that you don't need to generate those additional entries, if 'news' and 'gallery' will always be defined. And if you did need some customizable content for those pages, you could easily place that in the projects main entry.

For another example see this question regarding site structure.


Check what methods were used on element query I am creating plugin that provides helper methods for constructing search forms, filtering entry lists
by various params etc. And for that plugin, I need to be able to tell which methods were alredy used on element query object - because if you use same method twice, only second one will be taken into account.
So, it is possible to check if for example relatedTo was used on specific element query object already?

Most of the time, you can check if an ElementQuery parameter has been set by checking the corresponding property name: it will be null if the parameter hasn't been set, and have the previously set value otherwise.
Examples
This will output 6:
  {% set query = craft.entries().relatedTo(6) %}
  {{ dump(query.relatedTo) }}

This will output null:
  {% set query = craft.entries() %}
  {{ dump(query.relatedTo) }}


Editing assets custom fields in a full page publish form So this is just a question to see if something is actually doable.

When you double click an asset, you get a little modal opening where you can see see custom field attached to said asset.

Would it be possible (plugin work I'm guessing) to open this model as a full publish form like entries have..?

Just wondering... :)

Sure, this should be possible from a plugin. But you could also suggest this as a new core feature on the Craft Feedback page. I'd definitly vote for this! ;)


Filtering a Matrix block for future dates I'm struggling with filtering future dates in a Matrix block that contains a date field.

I would like to display 'Open' or 'Closed' as the result of a condition checking if the value of matrix rows is today, but I only want to run the condition against future dates in the matrix block.  Past dates should be ignored.

In all examples I have seen, the search logic is at declared at the entries-level, but this is a matrix block and I can't get the two to connect.

My code...

{% set closed = craft.block('closedDates')
    .type('closedDate')
    .date('>=' ~ today)
    .all() %}
{% for block in entry.closedDates.all() %}
    {% if today == closed %}

Closed

    {% else %}

Open

    {% endif %}
{% endfor %}```

The (matrix) field = closedDates, the block type = closedDate, the field = date.

I prefer a matrix field so I have only 1 entry.

Would appreciate any suggestions.

Thanks Marten, yes, that's correct.

After plugging away I have gotten it to work, but it's likely not as efficient as it could be...

{% set today = now|date('Y-m-d') %}
{% set todayday = now|date('l') %}
{% for block in entry.closedDates.date('>=' ~ today).limit(1) %}
    {% set closedDate = block.date.format('Y-m-d') %}
    {% if closedDate == today %}

It's a holiday, sorry, we're closed

    {% elseif todayday == 'Sunday' %}

It's {{ todayday }}, sorry, we're closed

    {% else %}

We're Open!

    {% endif %}
{% endfor %}

I added a check for whether today is Sunday, which is a normally closed day that would not be in the entry.

Doug


Problems exporting entries from ExpressionEngine 3 with Feed Me 2 beta plugin I am trying to export entries from my EE 3 install to Craft and import them using the Feed Me 2 beta plugin.

I believe I have followed the instructions on creating a template from within ExpressionEngine 3 with the export.php code from engram-design's github.  I have enabled PHP on the template from within EE.

When I run the mysite-url/export?id=1 file, it gives me this error page:

  {!-- ra:0000000004427cd500000000745c77df --}
  Notice

  Undefined property: EE_Functions::$EE

  ee/legacy/libraries/Functions.php(680) : eval()'d code, line 4 show details

  Severity: E_NOTICE
  Notice

  Trying to get property of non-object

  ee/legacy/libraries/Functions.php(680) : eval()'d code, line 4 show details

  Severity: E_NOTICE

  Fatal error: Uncaught Error: Call to a member function query() on null in /home/iwatermt/public_html/system/ee/legacy/libraries/Functions.php(680) : eval()'d code:4 Stack trace: #0 /home/iwatermt/public_html/system/ee/legacy/libraries/Functions.php(680): eval() #1 /home/iwatermt/public_html/system/ee/legacy/libraries/Template.php(1825): EE_Functions->evaluate('{!-- ra:0000000...') #2 /home/iwatermt/public_html/system/ee/legacy/libraries/Template.php(559): EE_Template->parse_template_php('{!-- ra:0000000...') #3 /home/iwatermt/public_html/system/ee/legacy/libraries/Template.php(237): EE_Template->parse('{!-- ra:0000000...', false, '1', false) #4 /home/iwatermt/public_html/system/ee/legacy/libraries/Template.php(168): EE_Template->fetch_and_parse('', Array, false) #5 /home/iwatermt/public_html/system/ee/legacy/libraries/Core.php(680): EE_Template->run_template_engine('', '') #6 /home/iwatermt/public_html/system/ee/legacy/controllers/ee.php(68): EE_Core->generate_page() #7 [internal function]: EE->index() #8 /home/iwatermt/p in /home/iwatermt/public_html/system/ee/legacy/libraries/Functions.php(680) : eval()'d code on line 4

Any ideas what I'm doing wrong?

Just some additional context here, we're referring to the guide for Feed Me 2 here, which leads to the Gist here. This Gist has not been tested with EE3, but should work just fine for EE2.

That Gist is really just a guide to get you started quickly with an export. It's in no way a requirement, as you can simply create your own JSON or XML templates in Expression Engine to output your content. Once that's done, its over to Feed Me to continue as normal.

You might like to refer to https://github.com/rsanchez/json or simply write your own JSON/XML templates.


Filtering with multiple criteria IM running into an issue where I have one page that lists latest restaurant entries in a given section. However I also have a form to filter the entries by postcode (a category element and by cuisine type (tag element).

I'm also required to give some rudimentary search functionality, which for now is governed by a seperate form. Both forms redirect to the same root page,

Here i my slimmed down code

{% set restaurants = craft.entries.section('restaurants').limit(null) %}
{% set locations = craft.categories.group('postcode').relatedTo(restaurants).limit(null) %}
{% set types = craft.tags.group('imageTags').relatedTo(restaurants).limit(null) %}

{% set l =  craft.request.getParam('l') %}
{% set t = craft.request.getParam('t') %}
{% set q = craft.request.getParam('q') %}

{% set locId = craft.categories.group('postcode').slug( l ).ids() %}
{% set locName = craft.categories.group('postcode').slug( l ).first.title %}
{% set typeId = craft.tags.group('imageTags').name( t ).ids()  %}

{% if (l or t) %}
    {% set relatedTo = ['and'] %}

    {% for id in locId %}
      {% set relatedTo = relatedTo|merge([id]) %}
    {% endfor %}

    {% for id in typeId %}
      {% set relatedTo = relatedTo|merge([id]) %}
    {% endfor %}

{% else %}

{% endif %}

{% set params = {
        section : 'restaurants',
        limit: 24,
        relatedTo: relatedTo ? relatedTo,
        search : q ? q,
        }
%}

{% set entries = craft.entries(params) %}

Pagination and for loop

{% paginate entries as galEntries %}

                {% for entry in galEntries %}

                    {% include 'restaurants/includes/_rest-item' %}

                {% endfor %}

{% endpaginate %}

My form elements

<form action="{{ url('restaurants') }}" method="get" action="" class="rest-filter">

            <label>Filter :</label>

            <select name="l" id="">
                <option value="">All Locations</option>

                {% for loc in locations %}
                <option value="{{ loc.slug }}" {{ loc.slug == l ? 'selected' }}>{{ loc.title }}</option>
                {% endfor %}

            </select>

            <select name="t" id="">
                <option value="">All Types</option>

            {% for type in types %}
                <option value="{{ type.name }}" {{ type.name == t  ? 'selected' }}>{{ type.name | capitalize }}</option>
            {% endfor %}

            </select>

            <input type="submit" value="Filter">

        </form>

        <form action="{{ url('restaurants') }}" method="get" action="" class="rest-search">
            <input type="text" name="q" class="text" value="{{ searchq }}" placeholder="Search for restaurants&hellip;" required />
                            <button type="submit"  class="ss-icon sn-search-button">Search</button>
        </form>

It works for the most part except for when I only filter by one element. Which throws the following error CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 1116 Too many tables; MySQL can only use 61 tables in a join

ANy ideas where I'm going wrong?

It looks like your loops are causing Yii to add joins to the craft_relations table once for each iteration of your loop.

{% for id in locId %}
  {% set relatedTo = relatedTo|merge([id]) %}
{% endfor %}

{% for id in typeId %}
  {% set relatedTo = relatedTo|merge([id]) %}
{% endfor %}

I would guess that either locId or typeId contains an array with many values. Try using {{ dump() }} or {{ locId | length }} to see how many values you are dealing with.

You might need to refine the query for each of these locId and typeId to ensure you get a single result.

Alternatively if you really want to search among that many records you might need to redesign your approach. Perhaps build an SQL query that achieves what you want first then work your way back from there.


How do I stop an element from populating? I have an event listener on elements.onPopulateElement, what I want is make a last minute decision if I want to populate or not. I have certain customfields on an admin CP user (eg company). All entries (in this case in the section "normalUsers", these are entries not actual users) have that same field company (companiesDropdown). I can detect in the code below that the entries' companiesDropdown field does not match the currentUsers companyDropdown field. But how can I tell the calling piece of code not the populate this element?

The following code it initialised in the plugin init method

private function _initEventPopulateNormalUser() {
    craft()->on('elements.onPopulateElement', function(Event $event) {
        $this->_getNormalUserSection();
        // HERE we check if we actually save a normalUser section entry, may be a bit brute force, we will polish that later
        $lCompanyIdEntry = $event->params['element']->getAttributes(array('sectionId'));
        if (! isset($lCompanyIdEntry['sectionId'])) return;
        if ($lCompanyIdEntry['sectionId'] != $this->mNormalUsersSection) return;

        $lCurrentUser = craft()->userSession->getUser();
        $lCompanyIdUser = $lCurrentUser->companiesDropdown[0]->id;

        $lEntryCompanyId = $event->params['element']->companiesDropdown->first()->id;

        if ($lCompanyIdUser != $lEntryCompanyId) {
            /*
                Somewhere here I want the element to be emptied and
                all functionality calling it, like a elementindex template,
                to not show the element in its listing
            */
        }
    });
}

private function _getNormalUserSection()
{
    if (is_null($this->mNormalUsersSection)) {
        $lSection = craft()->sections->getSectionByHandle('normalUsers');
        $this->mNormalUsersSection = $lSection->id;
    }
}

I am not really clear on what you are trying to do. My guess is you are trying to add your own validation to saving an element and trying to block that with your own custom checking of content values etc?

I don't think listening to the event will help as you cannot send a message to say this element is not valid from an event listener - the event does pass you a element that is about to be saved but you cant return the modified element from the listener.

What I would suggest is building a custom fieldType that contains some validation rules that peeks in at the content of other fields.

class My_CustomFieldType extends BaseFieldType
{

/**
 All the normal filetype stuff like name etc here.
*/

public function validate($value)
{
  $errors = parent::validate($value);

  if (!is_array($errors))
  {
    $errors = array();
  }

  // DO you logic here to test then set an $variable to true or false
  // if your conditions are met then add to the $errors array below if there is a problem.
  // if you want to access other field in this element do this:
  //
  //   $owner     = $this->element;
  //   $fieldData = $owner->getContent()->getAttribute(array('sectionId'));
  //

  if ($variable){
    $errors[] = Craft::t('You are not allowed to edit this element.');
  }

  if ($errors)
  {
    return $errors;
  }
  else
  {
    return true;
  }
}

}

Im thinking that this will be able to stop the entry being saved. Hopefully thats what you were looking for.


Error: Variable 'entry' does not exist - When adding homepage or other entry with URL I have an existing Craft Pro installation (v2.5.2757) that I'm using to drive an application.  The current install does not have a homepage or any entry associated with a URL. All requests are being handled by Dynamic Sites Routes (routes.php).

The site has been running without issue for months with edits being made to entries and content being updated in the application.

However, when I went to create a homepage for the site, I would receive the following error when the site loaded:

I confirmed that the 'This is for the homepage' was checked and that the correct 'Entry Template' was entered. I suspected that something in routes.php was causing the issue, but cleared that out and still received the error.  I tried clearing the caches as well but no luck.

After multiple hours of troubleshooting I'm at a loss.

After trying many different things I noticed that the background tasks que was backed up:

The queue was not moving. After a little research I came across the support ticket on craft on how to Resolve "Stuck" Background Tasks. After completing the couple steps of updating the stuck task status and restarting the queue, they all cleared out.

After this, my homepage was pulling in the entry properly and parsing the 'entry' variable without issue.

My guess is that if you create an entry while the background tasks queue is stuck you will run into this issue.


onAfterElementSave and new elements I have a field type that needs to do some processing, based on the data in the element it belongs to, when an element (entry, category, user, etc) is saved, and save it to the field. I'm hooking into onAfterElementSave and my code (simplified) is as follow:

public function onAfterElementSave()
{
    $fieldHandle = $this->model->handle;
    $elementId = $this->element->id;
    $elementType = $this->element->getElementType();
    $elementLocale = $this->element->locale;

    $criteria = craft()->elements->getCriteria($elementType);
    $criteria->id = $elementId;
    $criteria->locale = $elementLocale;
    $element = $criteria->first();

    if ($element!==null) {
        $myData = $element->title; // just an example
        if ($this->element->$fieldHandle != $myData) {
            $newData = array($fieldHandle => $myData);
            $element->setContent($newData);
            craft()->elements->saveElement($element);
        }
    }
}

This works fine on existing entries, but when I create new entries, $element is null, so the field isn't updated.

What's going on and how can I fix this? Isn't onAfterElementSave actually after element save? :)

You should be using it like this:

public function onAfterElementSave()
{
    $fieldHandle = $this->model->handle;
    if ($this->element!==null) {
        $myData = $this->element->title; // just an example
        if ($this->element->$fieldHandle != $myData) {
            $this->element->getContent()->setAttribute($fieldHandle, $myData);
            craft()->elements->saveElement($this->element);
        }
    }
}


Creating Drafts from Frontend Entry Form A two part question here.

When saving an entry from front-end entry form, can I designate it to save as a draft? If so, how?
If the answer to #1 is true, can I query against that to list only entries that are not in 'draft' mode.

Yes. Kind of. Have you had a look at the Entry Form example in the docs? It looks like Craft will always need an entry to begin with, so you can't have a draft version without first creating a normal entry. That being said, you can set this new this entry as disabled, which also can be queried against by using

craft.entries.status('disabled')
I cannot see a documented example on how to list drafted entries, though.

Either way, to build on the Entry Form example, instead of having the action set to entries/saveEntry, change it to entryRevisions/saveDraft.

Something like:

<input type="hidden" name="action" value="entryRevisions/saveDraft">
<input type="hidden" name="redirect" value="draft?id={id}&draft={draftId}">
<input type="hidden" name="sectionId" value="1" />
<input type="hidden" name="enabled" value="0">

Note that you're now posting to the EntryRevisionsController@saveDraft, and that I here set entries to be disabled by default.

Does having them disabled help you do what you want, or do they have to be in Draft mode for some reason?


How can I disable a category The green bubble (status is enabled) next to the category items in the category overview suggests that you can disabled/enable the category.

Is that possible? And if so, how?

I don't think you can disable categories with the current version of Craft. But you're right, it currently looks like that categories are prepared to get disabled.

Update:

Categories now have their own edit pages in Craft 2.3, where you can set the status. And you can even set it on a batch from the category index page!


Searching for users from a plugin I know how to find user by username and email. The problem comes when I need to query users based on a custom field.

Here is my scenario. I have about 2,000 users (part of a user group 'Affiliates') that I have created in our system. They have many custom fields including affiliateId and studentCount. I need to be able to execute two separate queries.

Find a single user by his/her affiliateId (int).
Query all users within the Affiliate group, order by studentCount asc and return the top record. (I need a user that has the fewest number of students).

I have searched docs and tried numerous combinations to query users. Any help at this point greatly appreciated. Is search the best way? If so, how do you order the search result?

Find a single user by his/her affiliateId (int).

$criteria = craft()->elements->getCriteria(ElementType::User);
$criteria->affiliateId = 1;
$users = $criteria->find();

  Query all users within the Affiliate group, order by studentCount asc and return the top record. (I need a user that has the fewest number of students).

$criteria = craft()->elements->getCriteria(ElementType::User);
$criteria->affiliateGroup = 'myGroup';
$criteria->order = 'studentCount ASC';
$criteria->limit = 1;
$users = $criteria->find();


How to read form response URL variables in template? I have a basic login form that's talking to a separate server that's out of my immediate control. I'm trying to read the responses it's sending in the URL within the Craft template.
For example...

http://www.adacore.com/login?login=incorrect

I've only ever done it before in PHP. Is it possible with Craft/Twig to access these variables directly? Or should I just read the craft.request.queryString?

And change the template accordingly as needed.

I'd go with the built in craft functions.

craft.request.getQuery('login')

craft.request documentation here


Add active class to nav created from globals I have a simple nav set up using Globals in a table, but I'm struggling to get it to add an active class to only the active page.

The below code adds the nav links, but I need to know what to add to it to get the active class on only the current page.

Need a little help please?

{% if mainNav.navitems|length %}
    {% for row in mainNav.navitems %}

{{ row.name }}

    {% endfor %}
{% endif %}

Depends on your URL structure ultimately and whether you have sub pages, but I think the easiest way to do it would be something like:

{% if mainNav.navitems|length %}
 {% for row in mainNav.navitems %}

     {{ row.name }}

 {% endfor %}
{% endif %}

You don't have to use the segment(1) method if it doesn't suit, take a look at the request docs to find something that suits your URLs


Deleting stale template stuck fatal memory error So all my background tasks Craft 3.2.6 seem to process ok except deleting stale template caches. Although craft is supposed to fail after 5 minutes, it fails instantly and the error log shows

Allowed memory size of 1610612736 bytes exhausted (tried to allocate 380928 bytes)

Server settings for memory are 512M and 300 second timeout. The error is instant - in that the error log is immediately populated on saving an entry - which makes me think that this is not the real problem. The server is running Nginx as a reverse proxy for apache and uses microcaching and I wonder if this might interfere with that one task?
Its a multisite with 8 languages and I guess quite heavy on the fields and image processing.
How can I debug this further - short of giving the whole dedicated box memory to this one site?

Anything in queue.log that gives us hints? Anyway if it is just the stale template caches task... I'd just truncate the cache tables manually (n.b.: truncate, not delete):

templatecaches

You only need to truncate that one cache table, because it'll cascade can remove the other related columns in templatecacheelements and templatecachequeries as well.

Then I'd truncate the queue table as well to remove the no longer needed stale template cache queue jobs.

Learn more:

Zen and the Art of Craft CMS Log File Reading
The Craft {% cache %} Tag In-Depth


Why is Craft extremely slow when using a shared, external database? Every now and then I work in developer teams where we all use our local development environment to work on the code, but share the same database on a remote server (like discussed here etc.).

Most of the time this will kill Crafts performance big time on our local machines. Requests take up to 30 seconds in the Control Panel and in the front-end as well.

Does anyone else encounter the same behavior and/or has an idea how to improve that? The logs don’t explain much.

Time:   25.85331s
(index):3505 Memory: 25,723Kb
(index):3506 Total Queries: 287
...

The site on the remote server is snappy like you would expect it.

My suggestion would be to do what is outlined in Database & Asset Syncing Between Environments in Craft CMS

It's a little bit of logistical organization coupled with some scripts to do the asset/db synchronization.

It's going to be much, much faster than working with a remote db... and safer, too.


How to get a product variant by SKU and get parent product details as well? I need to get a product variant as an object in Twig, as well as get its parents details (the product fields, not just the variant fields). My first guess starts with this:

{% set variant = craft.commerce.products.sku('my-sku-number') %}

But that doesn't work. I've also tried adding a the .first() limiter to the end. What am I missing?

Edit

Thoughts... should I be trying a search/find?

Untested, but something like this should work:

{% set products = craft.commerce.products({
  hasVariant: {sku: 'my-sku-number'}
}).first() %}

See: https://craftcommerce.com/docs/craft-commerce-products#hasvariant

If you only want to search the default variant of the product:

{% set products = craft.commerce.products({defaultSku : 'my-sku-number'}).first() %}

See: https://craftcommerce.com/docs/craft-commerce-products#defaultsku


Cache not invalidating when using pagination with cache Any idea why this code wouldn’t update/break the cache when new items are created?

Think perhaps the combination of my code for pagination, cache and the ability to also break for pending entries isn't correct and throwing it off...

{#
 # NEWS LISTING TEMPLATE
 # ---------------
 #}

{% extends "_layouts/main" %}

{# Get the most soonest upcoming pending entry for Cache breaking #}
{% set firstPending = craft.entries({
  section: 'news',
  status: 'pending',
  order: 'postDate asc'
}).first %}
{% set cacheUntil = firstPending ? firstPending.postDate : now|date_modify('+1 year') %}

{% set entries = craft.entries({
  section: 'news',
  order: 'postDate desc',
  limit: 6
}) %}

{# Get the articles and paginate them #}
{% paginate entries as pageInfo, pageEntries %}

{% block main %}
{% cache globally using key craft.request.path until cacheUntil %}

{{ entry.title }}

  {% for entry in pageEntries %}

         <time datetime="{{ entry.postDate|date('Y-m-d H:i:s') }}" class=" text-center mb-0 relative z-30 text-sm">

            {% include '_includes/svg/icon-date.svg' %}

            {{ helpers.dateFormatted(entry.postDate) }}

        </time>

            {{ entry.title }}

          view news

  {% endfor %}

{% endcache %}

{% include '_includes/partials/pagination' %}
{% endblock %}

One issue I'm seeing, is that you're currently using craft.request.path as your cache key – however, that property doesn't actually include the pagination segment (i.e. the pageTrigger; p2, p3 or the like), which means that you're currently going to see the same, cached content across all of your pages, because you're caching globally using an identical key (i.e. the root URI of your paginated template).

For Craft 2, AFAIK there's no single property or method on the HttpRequestService that will give you the full URL including the pagination segment – so you'll need to append the pagination number using craft.request.getPageNum():

{% cache globally using key craft.request.path ~ craft.request.getPageNum() until cacheUntil %}

For Craft 3, there's craft.app.request.getFullPath(), which does include the pagination segment – so the following should do the trick:

{% cache globally using key craft.app.request.getFullPath() until cacheUntil %}

As far as your solution for breaking the cache whenever "pending" entries are published, I'm not sure, but I'm not seeing any problems with it, at a glance.

The reason why you're not seeing the cache cleared when you create new entries, is probably because your second craft.entries query (the one that actually fetches the entries) is outside your {% cache %} tag (this would be a performance issue as well; your current code isn't really caching anything, except a bit of not too demanding Twig code). You should probably move the second craft.entries query, the {% paginate %} tag and the {% include '_includes/partials/pagination' %} statement inside the {% cache %} tag pair.

Also, you might want to re-think having a default cache duration of 1 year – that's a long time, and if your site builds up a lot of pages, it could balloon your database and possibly cause issues with stalled Deleting state template caches tasks. My advice would be to keep the duration to something sane like 1 month, max – there's little point in keeping year-old caches around, normally :)


How to set CRAFT_PLUGINS_PATH when invoking a console plugin action I have an install where I've moved the plugins/ directory outside of the craft/ directory. I've set CRAFT_PLUGINS_PATH in index.php and for web requests everything works fine.

Now I need to run a console command defined in a plugin. When I invoke the console from my shell, the default (incorrect) value for CRAFT_PLUGINS_PATH is set and my plugin's console command is not available.

I've currently worked around this by wrapping craft/app/etc/console/yiic.php, setting the constant before requiring yiic.php like so:

#!/usr/bin/env php
<?php

define('CRAFT_PLUGINS_PATH', realpath(dirname(__FILE__) . "/../plugins").'/');

require_once(dirname(__FILE__).'/../craft/app/etc/console/yiic.php');

Is there a better way to do this / am I missing something?

Nope, you've done it the best way in Craft 2.

The upcoming Craft 3 treats console requests as more first class citizens like requests so it's not nearly as awkward using them.


Change date language in a client version of craft I've installed a client version of craft. Apparently this is set in my local language (Dutch). First question is if that is something you can set to another language or must I install a Pro version for that? (Dutch is fine, but just would like to know)

Now, while the CP is in Dutch, if I parse the month, it is in English:

{{ entry.postDate.format('F') }}

So how can that be set in Dutch as well?

Found the solution in the twig docs, this appears to work:

{{ entry.postDate|date("F") }}

This uses the local date setup instead of the US setup

Still odd, that the standard language from the CP is not used?


Craft 3 Template - Getting Image URLs I am having a problem getting image urls from Craft 3. I understand that there is a new syntax but I cannot find the syntax to use. The craft 2 version of what I am trying to accomplish is

{% set heroImage = entry.heroImage.first().url %}

How do I achieve this in Craft 3?

Something like this should do it:

{% set heroImage = entry.heroImage.one().getUrl() %}

Although this probably works as well:

{% set heroImage = entry.heroImage.one().url %}

In Craft 3, .first() has been replaced with .one().  https://docs.craftcms.com/v3/changes-in-craft-3.html#query-methods


Session key not timing out when set in a plugin Here's some relevant detail from my config file:

    'rememberUsernameDuration' => false,
    'rememberedUserSessionDuration' => false,
    'userSessionDuration' => 'PT30S'

I'm expecting to see sessions timing out after 30 seconds but they don't seem to be, and if I remove that they are still valid beyond the default time.

Now, one thing I'm doing is setting a session directly in a plugin:

craft()->httpSession->add('myKey', 'myData');

And retrieving:

craft()->httpSession->get('myKey');

Am I missing something here? The config file definitely affects the Craft CMS login session as with the config above I get constant warnings to keep my session open in the CMS.

I think you're confusing Craft's userSessionDuration - which is the amount of time that you can stay logged into the control panel - with PHP's session (which is what craft()->httpSession is a wrapper for).

When you get logged out of the control panel, the former userSession is killed, but you still keep the same underlying PHP session ID and cookie.


How do I fix getTemplatesPath() / setTemplatesPath() deprecation errors? After updating to Craft 2.6.2778, my plugin is causing these deprecation errors:

PathService::getTemplatesPath() has been deprecated. Use TemplatesService::getTemplatesPath() or TemplatesService::getTemplateMode() instead.
PathService::setTemplatesPath() has been deprecated. Use TemplatesService::setTemplatesPath() or TemplatesService::setTemplateMode() instead.

How do I fix these?

PathsService::getTemplatesPath() and setTemplatesPath() were deprecated in Craft 2.6.2778. They were commonly used by Craft and plugins to render front-end templates from the Control Panel (or vise-versa).

We realized that there were cases where simply changing the root templates path was insufficient to emulate front-end requests in the CP, though. For example, if defaultTemplateExtensions was being overridden to:

'defaultTemplateExtensions' => array('htm'),

and the CP was trying to find a “test” template which was supposed to resolve to test.htm, a CP request would not actually be able to find the template, because CP requests are only looking for templates that end in .twig and .html.

So in 2.6.2778 we’ve resolved this with “template modes”, which provide an explicit way to tell Craft to render a front-end template vs. a CP template, rather than simply telling it to use a different base template directory.

Updating your plugin code is pretty simple. Just change things like this:

$oldPath = craft()->path->getTemplatesPath();
craft()->path->setTemplatesPath(craft()->path->getSiteTemplatesPath());

// ...

craft()->path->setTemplatesPath($oldPath);

to this:

$oldMode = craft()->templates->getTemplateMode();
craft()->templates->setTemplateMode(TemplateMode::Site);

// ...

craft()->templates->setTemplateMode($oldMode);


Is there any way of distinguishing whether a Date field's "time" is set or not? Date fields can have an optional "time" box. This works nicely in the admin, but I can't figure out whether there's any way to access date and time separately when accessing that field via a template.

The use case I'm thinking of is an "Event Date" field on an "Event" entry type, which may or may not have a time (because an event may be all day, or may be at a particular time). The way you'd format the date would likely differ depending on whether the time field was filled in or not.

I realise there are other ways of achieving this effect (eg. adding an extra "show time" switch), but I'm wondering specifically whether this is currently doable using just a standard Date field's optional time field?

The values entered in Date/Time fields are always saved as Craft datetime objects. This means it doesn't matter if you configure it to show the date, the time or both, Craft always saves them the same.

If you make it to show both, so you're able to enter a time for your events, you have to do this in the field's settings for all entries of your "Event" entry type. So to be able to switch input forms (a truly "optional" time field), you would actually need to have two entry types "Event with date" and "Event without date". Not the cleanest solution in my opinion.

What I'd do instead is to use two separate Date/Time fields, one configured to show the date and the other one to show the time. The client would only fill out the time field when necessary. And in the template you'd be able to check for an empty field.

{{ entry.eventDate|date('l, j. F Y') }}

{% if entry.eventTime %}
    {{ entry.eventTime|date('G:i') }}
{% endif %}


How can I add an option group to the select Macro from CraftCMS forms macros I'm trying to list out a select element with option groups and options using the awesome for macro that Pixel and Tonic created.

I have imported the macros and am calling the select one like so:

{{ forms.select({
          id: 'ccs',
          label: "Collection store:",
          class: '',
          name: 'fulfilmentStore',
          hasOptgroups: true,
          options: [
            {'optgroup':'North Island', 'value': 'NI1',},
            {'optgroup':'South Island'}
          ],
        }) }}

I pretty sure I have to have another lot of options sitting under the optGroup but can't get the syntax right.

Thanks in advance.

I had the same issue and managed to get it working like so:

{{ forms.selectField({
    options :[
     {optgroup:'North Island'},
     {label : 'Foo', value: 'foo'},
     {label : 'Bar', value: 'bar'},
     {optgroup:'South Island'},
     {label : 'Baz', value: 'baz'}
    ],
    name: name,
    value : value
})}}

So I guess in php (if working from a plugin) your array would need to look like:

array(
 array('optgroup' => 'North Island'),
 array('label' => 'Foo', 'value' => 'foo'),
 array('label' => 'Bar', 'value' => 'bar')
 array('optgroup' => 'South Island'),
 array('label' => 'Baz', 'value' => 'baz')
);

This then would output it like this in the admin area


searching assets - using related entry slug For a website I'm currently building I have a bunch of photos within an Assets folder (folder id of 5) that I have linked to "Photographer" entries within a "Photographers" structure.

I am now trying to search by the photographer slug in order to show photos for a specific photographer:

{% set searchQuery = "photographer::example-photographer" %}
{% set matchingPhotos = craft.assets.folderId(5).search(searchQuery) %}

Unfortunately however the matchingPhotos data set is always empty, even though I'm very sure that a photographer exists with a slug of example-photographer and there are photos linked to this photographer in the assets folder..

Is the way I'm trying to search correct?

Thanks!

I have not tried this against a working database but based on something very similar this should work:

{% set matchingPhotos = craft.assets.folderId(5).relatedTo({
  targetElement: craft.entries({
    slug : 'example-photographer',
    section : 'photographers'
  }),
  field : assetsPhotographerField
}) %}

In reverse what you are doing is getting all the photographer entries that match that slug that are related to the assets field in that specific folder id...does that make any sense?!

You may need to swap targetElement out for sourceElement - I never know which way round it should be!


Filtering multiple categories with URL queries I am trying to filter using categories. I have to filters type and size, in the url.

/products.html?size=medium,large&type=polo-shirt,jeans

In my template, I am doing the following

{% set sizes = craft.app.request.getQueryParam('size') %}
{% set types = craft.app.request.getQueryParam('type') %}

{% set relationParam = ['and'] %}

{# type #}
{% if types %}
  {% for i in types|split(",") %}
    {% set type = craft.categories.group('types').slug(i) %}
    {% set relationParam = relationParam|merge([{ targetElement: type }]) %}
  {% endfor %}
{% endif %}

{# sizes #}
{% if sizes %}
  {% for i in sizes|split(",") %}
    {% set size = craft.categories.group('sizes').slug(i) %}
    {% set relationParam = relationParam|merge([{ targetElement: size }]) %}
  {% endfor %}
{% endif %}

{# Avoid passing an array with `['and']` only #}
{% if relationParam|length < 2 %}
  {% set relationParam = '' %}
{% endif %}

{% set entries = craft.entries({
    section: 'products',
    relatedTo:  relationParam
}) %}

<div class="listings-container">
  {% if entries|length %}
    {% for entry in entries %}
        {% include "partials/_listing.html" %}
    {% endfor %}
  {% endif %}
</div>

The problem I'm having is as follows:

I click on “medium” in sizes, it shows all products that have the category “medium”
I then, click on “large” in sizes, it shows one result because it’s searching for products that have all 2 categories (“medium”, “large”)
I then click on "polo-shirt" in types, this now shows all products that have all 3 categories (“medium”, “large", "polo-shirt")
I then click on “jeans” in sizes, this now shows no results as its trying to find products that have all 4 categories (“medium”, “large", "polo-shirt", "jeans")

What I am trying to achieve is find all the products that are “medium” or “large" sizes and of "polo-shirt" or “jeans” types

The issue with your current code is that you're looping over each type/size category, creating a parameter in your relatedTo query for each one. This means that you're currently creating a query that looks like this:

relatedTo([
    'and',
    { targetElement: 1 },
    { targetElement: 2 },
    { targetElement: 3 },
    { targetElement: 4 }
])

...which, combined with the AND, means that Craft will only return entries that have all of these relations.

What you need to do, is to collect the IDs for each category group in an array, and add those arrays to the relatedTo query – i.e. create a query that looks like this:

relatedTo([
    'and',
    { targetElement: [1, 2] },
    { targetElement: [3, 4] }
])

The above query would solve your issue, because whenever Craft isn't given an and or or, it assumes or. This means that the above query is exactly the same as the below:

relatedTo([
    'and',
    { targetElement: ['or', 1, 2] },
    { targetElement: ['or', 3, 4] }
])

Essentially, the above query is saying: give me all entries that have a relation matching either the target element 1 OR 2, AND a relation matching either the target element 3 or 4.

Here's how the Twig to create a query like the above could look:

{# Create the base query #}
{% set query = craft.entries({ section: 'products' }) %}

{% set relatedTo = [] %}

{% set types = craft.app.request.getQueryParam('type')|split(',')|filter %}
{% if types %}
    {% set relatedTo = relatedTo|merge([{
        targetElement: craft.categories({ group: 'types', slug: types }).ids()
    }]) %}
{% endif %}

{% set sizes = craft.app.request.getQueryParam('size')|split(',')|filter %}
{% if sizes %}
    {% set relatedTo = relatedTo|merge([{
        targetElement: craft.categories({ group: 'sizes', slug: sizes }).ids()
    }]) %}
{% endif %}

{% if relatedTo|length > 1 %}
    {% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}

{% set entries = query.relatedTo(relatedTo ? relatedTo : null).all() %}

The above code should work for all the below use cases:

/products (returns all product entries)
/products?type=polo-shirt (returns all polo shirts)
/products?type=polo-shirt&size=small (returns all small polo shirts)
/products?type=polo-shirt,jeans&size=small (returns all small polo shirts and jeans)
/products?type=polo-shirt,jeans&size=small,medium (returns all small and medium polo shirts and jeans)

Note that the usage of ids() over all() in a few places (such as in the craft.categories queries to get the type and size categories) – this is a performance tweak (it's more effective to just pull the IDs than the entire model).

Also note that if you want to use pagination, just amend the final line to

{% paginate query.relatedTo(relatedTo ? relatedTo : null).limit(10) as pageInfo, pageEntries %}

Note: I originally typed out a completely different answer to this question, but after some helpful discussion with @Robin Schambach on Slack, we arrived on the above, IMO much better solution. Cheers Robin!


Sprout Forms beta to paid? I'm trying to update all my project plugins. Over a year ago I originally installed Sprout Forms beta as a trial and have since purchased the license.
When I run craft update I get

Your requirements could not be resolved to an installable set of packages.
Problem 1

barrelstrength/sprout-forms-range-slider 1.0.5 requires barrelstrength/sprout-forms ^3.0.0-beta.47 -> satisfiable by barrelstrength/sprout-forms[v3.x-dev].

barrelstrength/sprout-forms-range-slider 1.0.5 requires barrelstrength/sprout-forms ^3.0.0-beta.47 -> satisfiable by barrelstrength/sprout-forms[v3.x-dev].

Conclusion: don't install barrelstrength/sprout-forms v3.x-dev

Installation request for barrelstrength/sprout-forms-range-slider 1.0.5 -> satisfiable by barrelstrength/sprout-forms-range-slider[1.0.5].

<warning>Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.</warning>

I get this (or similar) message often. I want to resolve it once and for all. Do I just uninstall Sprout Forms beta? Someone reassure me I won't lose all my data.

Running composer commands will not affect the data in your Craft database directly. The only scenario that might update your data or schema would be if you added a new plugin that had an install migration or updated a plugin or module that included an migration. These migrations would not get run during your composer command, but could get triggered when you visited the /admin area of your control panel or ran a command like ./craft migrate/all.

Due to the timing of multiple major releases between Sprout, modules that Sprout depends on, and Craft (Craft 3.1 should have also been considered a major release), there are several tricky upgrade scenarios between Sprout and Craft between the Craft 3.0 and 3.1 set of dates.

The easiest path around these is to upgrade to the latest of everything all at once. composer update alone will not upgrade to the latest major release, only to the latest releases defined in your composer.json. To make sure you are upgrading to the latest of everything:

Review each package in the require array of your composer.json
Update each package to the latest version release.
Once all packages are defined with their latest versions, run composer update

Regarding version constraints in your composer.json file, you'll probably want to require an explicit version of Craft and us the Caret Version Range for plugins:

{
  ...
  "require": {
    "craftcms/cms": "3.1.23",
    "vlucas/phpdotenv": "^2.4.0",
    "barrelstrength/sprout-forms": "^3.0.0-beta.50"
    "barrelstrength/sprout-forms-range-slider": "^1.5.0"
  }
  ...
}

By updating to the latest versions of everything all at once, you'll ensure that the major releases of supporting modules that may be used by more than one plugin are updated and resolved together.