Craft CMS Backing React FE using the element API I am hoping to create a React Site that uses Craft CMS and the Element API as a backend.  However, I know I will need to authenticate users and be posting data back into Craft which I believe the Element API doesn't support.

I was hoping someone might be able to talk me through if authenticating users is possible using a plugin and craft session service and if so what would be the general strategy given that this would be (at least in development) across two origins.

stage.project.com // craft cms w/ element api
localhost:3000 // react development

I haven't found any plugins out there that do just this, but it seems like it would be possible with a craft plugin.  Just wanted to confirm that this is possible and get a sense of how before investing the time.

I created a react native app with Craft CMS (still version 2.6) as backend using the Element API. But you're right, user authentication is not supported by the Element API. However, I created my own plugin to get user authenticated. I even create user and update their details through the app.

But storing user sessions in React Native is another story. That has nothing to do with Craft or any other CMS system cause you can't get server session to React Native. You need to write your own session handler. I remember a good blog post about that topic: https://auth0.com/blog/adding-authentication-to-react-native-using-jwt/.

There also might be other solutions because this could be a little bit overkill.

Hope this helps you as a first start.


Render Field from FieldHandler in FreeForm Craft CMS Can we render some particular field from our FreeForm plugin ?

If yes, how can we achieve it ?

To pull an individual field from a form, use the form.get("myFieldHandle") method.

Simple example using the field.render() method:

{% set form = craft.freeform.form("myFormHandle") %}

{{ form.get("firstName").render() }}

Or a more custom example:

{% set form = craft.freeform.form("myFormHandle") %}
{% set firstName = form.get("firstName") %}

<label>{{ firstName.label }}</label>
<input name="{{ firstName.handle }}" value="{{ firstName.value }}" />
{{ firstName.renderErrors() }}

Examples from Freeform documentation

Note from the Freeform's docs:

  When using with HTML blocks or Submit buttons, use the hash value provided in property editor in Composer as the handle. Example code would look like:
  {{ form.get('Ajx7jNxXL').render }}


Passing a variable to an include I have a category listing template where I'm having trouble passing in some variables.

top of the template I have this snippet where I'll set the channel and category field name based on the segment

{% set segment = craft.request.segment(1) %}

{# set channel based on segment_1 #}
{% if segment == "business-intel" %}
    {% set channelName = "businessIntel" %}
    {% set categoryName = "businessIntelCategories" %}
{% elseif segment == "foo" %}
    {# do nothing #}
{% endif %}
{# end set channel #}

Then further down the template I have this:

{% include ('_includes/cat_sub_with_article') with { 'categoryName' : categoryName } %}

And then the included template looks like this:

 {% for category, catEntries in entries | group('categoryName.first().title') %}

{{ category }}

    {% set featuredThumb = {
        mode: 'crop',
        width: 370,
        height: 168,
        quality: 90,
        position: 'top-center'
      } %}

        {% for entry in catEntries %}

                {% set image = entry.featuredImage.first() %}

                    {{ entry.postDate.format('M d, Y') }}

{{ entry.title }}

{{ entry.featuredSummary }}

Learn More

        {% endfor %}

 {% endfor %}

If I hardcode the category field name into line 1 of the above block everything works. However if I try to use the passed varialbe. I get this error, which I don't know how to fix:

  Craft\EntryModel and its behaviors do not have a method or closure
  named "categoryName".

You set the variable that your are passing in to your include to a string. And in your include you try to access it like an object.

The variable which you’re passing in to your include is set to a string 'businessIntelCategories'. And in your include you try access a object property dynamically using that string which doesn’t work.

Use this code instead.

{% set entriesGrouped = entries|group('{ object[categoryName].first().title }') %}

Here’s an attempt to explain the syntax.

First you should have a look at the Craft documentation for Dynamic Entry Titles. You see that you can dynamically generate titles with a normal Twig template {{ object.property }} or a special shortcut syntax {property}.

The group filter uses the same internal functionality to render the string that you pass in as a filter argument group('my twig template string'). The syntax has to be a bit different though, because group already adds one pair of curly braces for you.

So usually you echo the title property like so

{{ object.title }}

and in the context of the group filter you can do both of the following.

{% set entriesGrouped = entries|group('title') %}

{% set entriesGrouped = entries|group('{ object.title }') %}

The first syntax, which uses the shortcut syntax, is documented and fine for most cases. But you can’t use it if you need to do something a bit more advanced, like using a ternary operator or in your case access the object object differently.

So to wrap this up, the syntax for group('{ object[categoryName].first().title }') is confusing. But what it does is to render the following Twig code for each element of your array of entries (each entry is passed in as the variable object).

{{ object[categoryName].first().title }}

If you don’t like the object['propertyName'] syntax, you can use the attribute function instead.

{{ attribute(object, categoryName).first().title }}


Remove trailing comma from an output loop? I want to create a comma separated list of categories to which an entry belongs, but without the trailing slash on the last element... can't work out how to remove it!

{% for category in entry.mediaType %}
    <b>{{ category.title }}</b>,
{% endfor %}

There is a special tag inside for loops you can use for this

{% for category in entry.mediaType %}
    {{ category.title }},
    {% if loop.last %}
        {{ category.title }}
    {% endif %}
{% endfor %}

Full list here


Restricted front-end access As per the flow diagram below I want to restrict access for users outside my network. However rather then completely stopping external access I want them to be redirected to a login page where they will be required to login via google, there email address will have to be within the organisation to be granted access.

Any help/advice much appreciated.

Is the physical web server inside your network as well? As mcclaskiem mentioned,  check to see if the IP address is within the subnet of your network and then allow access that way.

You may want to have the web server listen on another hostname that's not accessible outside the network and allow access to that vhost unconditionally (but do that at the firewall level). The would be the securest way to allow access within your network without logging in. IP addresses can be spoofed with varying threat so I'd be wary of allowing anyone to authenticate via just an IP Address if your webserver is located off-premise.

Dealing with google auth on the "other side" would obviously require a plugin for people attempting to connect outside the network.

Here's another thread for further reading.


Is it possible to create this type of custom field? We need a field that holds assigned postal codes. Each Location entry will have this field and I'd like to have the following:

Buttons to assign postals by city, state, county (data grabbed from table I have)
on button click, the postal codes will be added to the actual field

I'm not sure if this is possible with a field or if I have to do this on a separate CP page

EDIT: I'll be more descriptive

We would have 4 buttons and 4 selects. The first select (State) will auto choose the state that the Location entry has on another field.

The 3 other fields (city, state, zips) will populate based on the proceeding choices.

So if the state is PA, the other 3 selects will populate only counties, cities, and zips located in that state.

There are buttons underneath each to assign either all zips in a state, county, city, or a single zip.

When they press add, I'd like to add to a table with the columns

State, County, City, Zip

I'd also like to have an option to add/remove assigned zips simply from a CSV in a textarea.

it sounds quite complicated, but I've done this exact thing on other apps...just not inside craft.

I don't fully understand it. But since modules can quite freely design their fields in the CP, this should be possible with a custom field type.

It sounds to me like a variation of the select field.


What are the cache options available to a Craft installation? Reading the Craft docs and other related questions on Stack Exchange there seems to be a lot questions about caching, what it does under different scenarios and how to best utilize it.  Is there an overview of all of the different caching options available in a Craft installation?

There are many different caching levels on a web server, some of them are Craft-specific, and some have nothing to do with Craft, but they often get confused.  In addition to that, some of them you have control over, and some of them you get for free.

PHP Accelerators

These are web server extensions installed to provide byte-level PHP caching so that PHP doesn't have to "re-compile" an entire PHP script the next time it is requested.  If you are on PHP 5.5+, most *nix distributions have OpCache enabled by default.  The settings to control how these extensions behave (TTL, cache busting, etc.) are extension specific.
These extensions are a frequent cause of "I've changed my PHP file, but it appears to be using old code" headaches.

Memoization

Craft (and probably many Craft plugins) employ a technique called memoization where the results of an expensive method call get cached so that, on that same request, future calls to that method can read from the cache instead of executing the same expensive code over again.

Native Twig caching

Twig, the templating language Craft uses, will take a given Twig template and "compile" it down to PHP code so that subsequent requests to that template will execute the (much faster) PHP directly instead of parsing the Twig template.  Craft stores the compiled templates in the Craft storage/runtime/compiled_templates folder by default.  If devMode is off, a compiled template will be busted when its source Twig template file's date modified date has changed.  If devMode is enabled, the Twig will compile the template on every request.

Craft template {% cache %} tags

Craft provides its own (not native to Twig) {% cache %} tags for templates.  The template cache tags store its data to wherever Craft is configured to store its general-purpose data caches. By default, that is the file system, but that can be overwritten to other data stores like the database, redis, etc.
Since using this tag involves database queries, you want to be smart about using it.  It can be detrimental to cache template code that doesn't generate any database queries.  The {% cache %} tag will only cache HTML output, so it also makes no sense to cache Twig variables.
The {% cache %} tag will keep track of any Craft elements (entries, assets, users, tags, etc.) inside of it so that if any of them change, it knows to bust their caches automatically.

General purpose data caching

Craft takes advantage of Yii's supplied caching techniques and exposes them via the Cache service class at Craft::$app->getCache() so that Craft and any plugins can have a place to store arbitrary cached data.  By default, Craft::$app->getCache() uses [file based caching][14], which gets saved to Craft's storage/runtime/cache folder, but Craft::$app->getCache() can be configured to save to many external sources.
These cache durations are controlled by the cacheDuration config setting.

External HTML caching services

You can configure applications like Varnish (and similar cloud-based services like CloudFlare) to provide static/partial page HTML caching to specific URLs with their cache-busting logic.
No doubt missing some other caching methods, but probably worth noting that it's a good general rule of thumb to not double up on caching strategies.  For example, the complexity of debugging caching grows exponentially when using Varnish with byte-level caching and the {% cache %} tag and you quickly get diminishing returns performance-wise.
There are many plugins in the Plugin Store that interact with Craft across the different caching levels in Craft to modifying existing or add new caching functionality.


Checkbox profile fields front end show labels in cp I have some custom profile fields saved on registration in the front end.
They are checkboxes and only submit on front end reg when they are set as an array. eg:

<div class="checkbox">
    <label>
        <input type="hidden" name="fields[phoneOptIn]" value="">

        {% set chbox = craft.fields.getFieldByHandle('phoneOptIn').getFieldType.options %}

        {% for option in chbox %}
            <input type="checkbox" name="fields[phoneOptIn][]" value="{{ option.value }}"  tabindex="29">
            {{ option.label }}
        {% endfor %}
    </label>
</div>

"fields[phoneOptIn][]" only works to save with "[]" in name.

The problem in the CP user display is that when configuring to show the check box fields in the CP, the values of the checkboxes are not shown. What is displayed instead is the labels. eg:

Is this a bug? Really want to see values in backend not labels.

This is the expected behaviour. It makes perfect sense to display the human-friendly checkbox label on an admin screen, rather than the (potentially obtuse) checkbox value.

To confirm that this isn't simply an issue with your form, I did the following:

Created a test checkbox field with three options.
Added the test checkbox field to the custom user fields.
Set the checkbox field to display on the users list page.
Edited a user via the control panel, and selected some of the checkbox items.
Returned to the users list page to view the results.

As expected, the users list page displayed the labels of the selected items.

The only difference appears to be that in my installation the selected items are displayed in a single column, as a comma-delimited list, whereas in your example each checkbox item receives its own column.

Here's a screenshot:

It's also worth mentioning that you are correct in stating that the checkbox option fields need to include the [] in the name. That's exactly how the admin forms work too.


Commerce - How can I get the values of a category field from a plugin? I have a purchasable that has a categories field. When the user visits the cart, I inspect the cart via my plugin.

I am able to see the field, but I am stumbling on how to actually check the values of the field (get the chosen categories).

MyPlugin

...

$lineItems = $cart->getLineItems();

if ($lineItems) {
    foreach ($lineItems as $lineItem) {
        error_log(print_r($lineItem->purchasable, true));
    }
} else {
    // The cart is empty.
}

...

...
[myCategoryField] => Array
    (
        [model] =>
        [column] => text
        [type] => mixed
        [label] => My Category
    )
...

If I go a step further into the field:

...
error_log(print_r($lineItem->purchasable->myCategoryField, true));

I can get something like this:

[relatedTo] => Array
    (
        [sourceElement] => 174
        [sourceLocale] => en_us
        [field] => 25
    )

This is the correct field ID, but I'm not sure how to get to the values of the category field. For example "Category A", "Category B".

Thank you for any suggestions!

MyPlugin.php

$record = MyPluginRecord::model()->findByPk($user->id);

if ($record) {
    $field = $record->getAttributes(); // The plugin model.

    if ($lineItems) {
        foreach ($lineItems as $lineItem) {
            foreach ($lineItem->purchasable->product->myCategoryField as $category) {
                error_log($category->title); // My Category
            }
        }
    } else {
        // The cart is empty.
    }
} else {
    // Product not found in cart.
}


How to understand this exception error in context? I have been trying to fine tune the performance of a heavy page:
see Category query very slow on multilingual site

It appears that my initial hope of getting the page under 2 seconds was not quite accurate.
After cache tagging the page the performance was great.
However I had left out an {% endcache %} tag - ie not closed it. This makes the performance in the network inspector less than 1 sec to load. Very nice.

However I am using Nginx microcaching set to 1 sec. As soon as I cleared the Nginx cache i started seeing the error in my template tag that was missing the closing cache tag.

So why does it load lightning fast with an unclosed tag?

When experimenting with placement of cache tags, I also managed to generate the following error in dev mode:

An exception has been thrown during the compilation of a template
("Node "body" does not exist for Node "Craft\Cache_Node".").

I was wondering what exactly this means / indicates and if there is some interaction effect I am getting between craft cache and my Nginx server cache?

If you leave off the closing {% endcache %} tag, then you should always get the error that you received with devMode enabled.  If devMode is disabled, you'll get the default 500 error template.

I would disable the nginx microcaching while you're testing this to make sure that you're not getting erroneous caching results.


Charge plugin redirecting to page with "1" on it I've been trying to figure this out for a couple days now and haven't been able to. I'm using the Squarebit Charge plugin to accept donations on a website.

I have everything working fine on my local Vagrant machine. However, when I push it to my staging server, the redirect to the "thank you" page never works. Instead of redirecting to the thank you page, the Donate page is reloaded and displays a single "1".

Does anyone have any ideas? I do get a TypeError in console because the card expiry date field had to be changed. Stripe would complain that the card exp_month field wasn't present so I had to create that field. I get the error on my Vagrant box too so I don't think it's causing the process to fail.

My staging link is here: kb.topsdev.net/donate

Any help is really appreciated.

I'm the developer of Charge.

I've never directly observed that behaviour, but it sounds like it's most likely hitting a redirect failure and falling back to some base success/fail responses.

The most likely cause of that, is that you've not specified the default success route in the Charge settings. You'll find that under Charge > Settings > General > Success URL.

In there, you can specify the exact redirect route to push a user to after a successful transaction.

A common format would be something like thanks/{slug}, and load up your receipt template, on a path like payments/receipt etc..

Alternatively you can supply a hidden input on the form called redirect with the url you'd want the user redirected to after the transaction completes.


How to use the Element API plugin only when logged in? I want to use the Element API plugin, but the content must only be visible when users are logged in. How I do this for a section? Is it possible to use requirelogin or currentUser for this?

If you want to restrict the whole API, you can add some code to the top of your /config/elementapi.php file (right below namespace Craft;) that checks for a logged in user, and ends the request with an appropriate status if the user isn't logged in:

<?php
namespace Craft;

$user = craft()->userSession->getUser();
if (!$user) {
    HeaderHelper::setHeader(array('status' => 404));
    craft()->end();
}

Note that you can replace the 404 with another HTTP status code if you want to; pick whatever feels appropriate to your app.

If you want to restrict specific endpoints, but leave others open, the best approach is probably to wrap the above code in a function, i.e.

<?php
namespace Craft;

function requireLogin() {
    $user = craft()->userSession->getUser();
    if (!$user) {
        HeaderHelper::setHeader(array('status' => 404));
        craft()->end();
    }
}

return [
    ...

Then, it's simply a matter of calling this function from your restricted endpoints' transformer callbacks:

'news.json' => [
    'elementType' => 'Entry',
    'criteria' => ['section' => 'news'],
    'transformer' => function(EntryModel $entry) {

        // This endpoint should be restricted
        requireLogin();

        return [
            'title' => $entry->title,
            ...
        ];

    },
],
...


Where to enter URL for Live Preview in Craft 3? Pretty sure I've seen where you can enter the URL for an entry type so Craft knows what page to display during live preview. But I can't find that now. I'm looking in the entry types, and I can't think of anywhere else it would be.

Am I going crazy? Where is that feature?

I see a field in the section settings called "Preview Targets". It says:

  Additional locations that should be available for previewing entries
  in this section.

If this is it, the term "Additional" is a bit misleading.

The "Entry URI Format" in Section settings is used for Live Preview by default. If you add additional "Preview Targets," then the Entry URI Format will show up as "Primary entry page" in the list of Live Preview options.

Preview Targets are "additional" because they aren't required and not used for template routing, unlike the "Entry URI Format" above it. They're only used for Live Preview mode.

The Craft docs on Preview Targets use the example of a blog post that you want to preview how it will look as an excerpt on the homepage or in a listing page like the blog index in addition to its main entry page.


Get discount data from adjustment object Is it possible to get, from one of the cart.adjustments of type discount, whether the discount was added with a coupon code, and if so, what that coupon code is?

An adjustment will take a snapshot of the source object that contained the rule for its application.

So the following is an array of a discounts attribute if you have a discount adjustment

{{ adjustment.sourceSnapshot }}

You could get the coupon code with

{{ adjustment.sourceSnapshot.code }}


How to skip fragments of code in Matrix? I fell like I'm missing an obvious thing, how to end block before I will start the new one?

{% for block in entry.myMatrixField %}

{% if block.type == "heading" %}

{{ block.heading }}

I don't want this text to be part of heading block

{% elseif block.type == "text" %}

    {{ block.text|markdown }}

{% endif %}

{% endfor %}

edit: here is my real code:

{# "Check" section #}

{# "Check" block #}
            {% elseif block.type == "check" %}

{{ block.checkHeader }}

{{ block.checkText }}

{# In this place block "check" should end #}

{# end of "check" section#}

{#beginning of "gallery section" #}

Next look at this cool stuff

{# "gallery" block #}
            {% elseif block.type == "gallery" %}

{{ block.imageHeader }}

{{ block.imageText }}

{# In this place block "gallery" should end #}

See More

Your question is rather vaguely but if I'm right:

You could put an "if" statement around your "h1" element like,

{% if loop.first %}
This text will only be used if the lightswitch is active
{% endif %}

Or add a field to the block. Preferably a lightswitch. Which if enabled will show the H1 tag and wont if you dont activate it.

{% if block.lightSwitch %}
This text will only be used in the first loop
{% endif %}


Any documentation about the elementindex template available? I'm trying to customise the elementindex view. For example I want to filter out some entries that don't match certain criteria. Also if possible add some hooks to the read calls. Is there any documentation available about this?

Thanks in advance

Your best bet on learning element type templating is to look at Craft template itself. Element types use the following template for controlling the layout:

craft/app/templates/_layouts/elementindex.html

However, its important to know that you should only be using the elementindexif you are creating a custom element type.

As far as the element index, a good portion of the options are actually set through Javascript by Craft using Garnish. If you look at the following code, you can see that the status, template side, is set via Garnish.

{% block initJs %}
Craft.createElementIndex('{{ elementTypeClass }}', $('#main'), {
    context:        '{{ context }}',
    showStatusMenu: {{ showStatusMenu is defined ? showStatusMenu|json_encode|raw : "'auto'" }},
    showLocaleMenu: {{ showLocaleMenu is defined ? showLocaleMenu|json_encode|raw : "'auto'" }},
    storageKey:     'elementindex.{{ elementTypeClass }}',
    criteria:       { localeEnabled: null }
});
{% endblock %}

You can set custom statuses on your element type, if that is the route you are looking to travel. I have dug into element types and custom statuses on these posts:

How to integrate custom statuses in a workflow style custom element type?
Best way to use custom enums in my plugin templates?
Best way to implement my own statuses on my custom element type?

As for Craft documentation, they are working on it still and the only thing available right now is this:

Creating new Element Types


Devmode entry variable NULL When I'm using devmode to dump out a twig variable the site will no longer work saying that entry = NULL.

This only happens when I have some twig logic at the top of my template e.g.

    {% set metaDescription = entry.metaDescription %}
    {% set metaPageTitle = entry.metaPageTitle %}

do I need to move this logic elsewhere in order for devMode to work?

This is because I was on a category page not an entry page, so the entry variable didn't exist!


Express Server posting user data to Craft First of all let me just say that I have only been using Craft for a few months and still has allot to learn about it.

My issue is quite simple in that I have an express server running to display the data on the front-end. I am using Craft as a solution for the client to add or change content on their site. This is working well. I am even able to use your Element API to retrieve data from Craft and together with my Express server I can manipulate and display content where it is needed.

My client has now recently asked me to add a section that would allow users to register users and this is where I am starting to have problems. Or at least, this is where I need some expert knowledge.

I would just like to save the user data via post. I have looked at a few options out there but honestly I don't want to build a plugin to handle this. Is there a way I could post and save data to Craft without it being too complicated?

You'd normally use a front-end form to create users on a Craft site. You can add fields to this form to submit more than the standard set of fields. I believe you could use the same form within your Express app. You are just sending the data via a POST request.

Check the documentation for an example.


Problem with user activation links I'm having a pretty consistent issue with the user activation links sent out in the registration emails. Having a hard time diagnosing because I can't replicate it myself, but getting several users a day contacting saying their link doesn't work. In the logs I see this:

2016/10/24 16:03:59 [warning] [application] The verification code (7TU6Z3adW6CXBmPjmgYqpJI9BeNi3cLN) given for userId: 44253 does not match the hash in the database.

I have the validationKey set in the config file and not sure if that may affect it(such as invalid characters in it), or maybe something is causing the verification code to include incorrect characters in the URL that is causing it to be truncated or read incorrectly when decoding?

The users email the end client directly, so often by the time they send me the info along, they've already manually activated them so I can't further troubleshoot.

Any ideas?

Just to add; we are apparently seeing this issue too. Or to be more clear: we haven't seen it ourselves, but our client keeps getting reports from their customers that Activation links are not working - giving "error messages" when clicked. We've then had the trouble mail forwarded to us, we've clicked the link, and it's been fine.

Looks like there is some mail client out there mangling the activation links. Which is bad. And we don't know of a solution.

The codes are not expired.


Adding a County input field into Address in Craft Commerce Any ideas how to add custom fields to the address Model?

I want to add a free text input for County, so that it can be used for the equivalent in any country, as the shop sells to more than UK.

Upon doing so, I get the following error when trying to edit an address;

Craft\Commerce_AddressModel and its behaviors do not have a method or closure named "county".

Currently (Dec '16) - custom fields are not able to be used with addresses in Commerce.

Your best best is probably to add a field to your orders and pretend it's on addresses, but obviously if you deal with multiple addresses, as you probably do, that gets a bit tricky quickly.  You could implement a plugin that has it's own table of records that associate addressIDs with a county field.

I am pretty sure it's on the Commerce radar but you're a bit stuck right now I think, without some extra work.


Null Length Error on Craft Version 3 I am not super fluent in Twig and I inherited a website from a company that built the site in Craft v2. The site was upgraded to version 3 of Craft recently and one of the template pages is throwing an error where on version 2 of Craft it was not. Code:
{% set faqs = craft.entries.section('FAQ').search('slug::' ~ entry.slug).first() %}

{% if faqs.questionsanswers|length > 0 %}
  FAQs
{% endif %}

This is throwing an error: Impossible to access an attribute ("questionsanswers") on a null variable.
So since it was returning null I changed the code to this which seems to be working:
{% if faqs is not null and faqs is not empty %}
  FAQs
{% endif %}

My question is why would this code not work for version 3 when it was working correctly for version 2? Why would the length return null instead of 0? I did not do the upgrade but maybe something was missed? (This is a template page and some of the entries do have FAQs and some don't) Or perhaps something with the structure of the version 3 of Craft has changed? Lastly if there is a better way to do/code what is being done I would love to hear it.

The error tells you that faqs is null. As you're using the first() function to execute your element query, this means that your query has no matches.

Are you sure that the section handle (FAQ) is in uppercase?
The correct way to create an entry query in Craft 3 is craft.entries(). Although it may work without the parenthesis, it can't hurt to add them.
You can replace .search('slug::' ~ entry.slug)  with .slug(entry.slug). It's easier to understand and should have the same effect.
Are you sure that the query always returned something in Craft 2? It may be worth adding a null check to avoid the error: {% if faqs and faqs.questionsanswers and faqs.questionsanswers|length > 0 %}


How do I reference the settings of my plugin from a template? This question and answer explains how to access a plugin's settings from elsewhere in the plugin's PHP code.

How would I access these settings from within a template? Or this impossible and I'd have to pass the values into the template when rendering it? ie, in a Controller action:

$settings = craft()->plugins->getPlugin('myPlugin')->getSettings();
$templateVars = array(
    'foo' => $settings->foo,
);
$this->renderTemplate('myPlugin/templateName', $templateVars);

In more recent versions of Craft 2.x (I forget when it was added), you can simply do this without needing a plugin:

craft.config.get('setting', 'plugin')

Where 'plugin' is the name of your plugin's config file (sans .php).

If you want to do this in Craft 3, you'd do:

craft.app.getModule('pluginHandle').getSettings().mySetting


Reduce Database size I would like to reduce the size of a site's database. The reasons are as follows:

To speed up the syncing process from live to local
To hopefully speed up sql query execution time (an experiment)

I have many sections and hundreds upon hundreds of entries, all with versioning enabled. My question is, is there a way to 'trim the fat', or to put it better perhaps remove stale old version types (perhaps just keep the 5 most revisions for each entry)?

Thanks in advance for you help!

If you're specifically looking to trim the entryversions table, there is a plugin that can automate the process for you.

Beyond that, you can safely truncate these tables as well when moving between environments:

assetindexdata
assettransformindex
sessions
templatecaches
templatecachecriteria
templatecacheelements


What does the CategorySources plugin do? A little off-topic, but wondering if someone can shed some light on the CategorySources plugin.

A client site has hundreds of articles (in an 'articles' structure) that are currently assigned to categories via a categories field. It would be great if they could view the articles by category (which I assume is what the plugin is for?). I installed, activated, and selected the category groups in the plugin settings, but I'm not seeing any obvious changes to the CP. Where are these mysterious category sources? Or am I missing the point? The plugin docs are a bit light on specifics.

IMO, the plugin serves two purposes... one of which is simply to serve as an example for how to use the modifyEntrySources hook.

Beyond that, the plugin will provide you with an alternative way to view your Entries. You'll notice that the categories which you selected now appear as entry sources on the left sidebar under the Entries tab.

Oh, and Craft 2.3.2629 is required... that's when modifyEntrySources was introduced.


Using a Matrix field to create a homepage that shows entries from sections and categories I have set up 3 channel types - A, B & C and a Category. Each entry type shares the same pool of categories.
On the home page, I have created a Matrix field (homeSections) with a block type heroSections that has A, B & C in a dropdown (mediaType) and a Category select (homeSection), so I can have "select mediaType A that has Category 1" as an option.
This is what I have so far
{% set entry = craft.entries().section('home').one() %}

{% for block in entry.homeSections.all() %}

    {% switch block.type %}

    {% case "heroSections" %}

        {% for entry in block.homeSection.all() %}

            <h3>{{ entry.title }} {{ block.mediaType.label }}</h3>

            {% set section = block.mediaType %}
            {% set category = entry.slug %}
            {% set entries = craft.entries.section(section).limit(4).orderby('postDate desc').all() %}

            {% include '_includes/listing' %}
        {% endfor %}

{% endswitch %}

{% endfor %}

but I can't figure out how to then filter the results so that the category is applied to the entries returned.

I figured it out.
I changed
{% set category = entry.slug %}
{% set entries = craft.entries.section(section).limit(4).orderby('postDate desc').all() %}

to
{% set category = craft.categories.slug(entry.slug) %}
{% set entries = craft.entries.section(section).relatedTo(category).limit(4).orderby('postDate desc').all() %}

and it shows up as expected.


Blank Screen on Site Search for some Search Terms We are getting a really odd result when doing certain site searches.

The search function loads a completely blank page when we search for the word Anaerobic; Looking at the source some of the template code is rendered but not all of it.

Anaerobic – blank page
Anaerobic digestion – blank page
Anaerobic Digestion Development Centre – returns results fine

Failed Search Terms

https://www.uk-cpi.com/search/results?q=Anaerobic+digestion

https://www.uk-cpi.com/search/results?q=Anaerobic

Working Search Terms

https://www.uk-cpi.com/search/results?q=Anaerobic+Digestion+Development+Centre

It seems to work fine locally - does anyone have any idea what could be causing the issue?

Thanks in advance,

Dan

Are you doing any caching on this site? If you haven't tried already, clear all the caches and run the query again in  Utilities > Clear Caches

I did some test searches, and it appeared to be the exact search term that's failing rather than content that's returned by that search term, which makes me wonder about a corrupt cache.


Displaying one site's content in another site (Craft 3 Multi-site) I've got a bunch of entries in Site 1. I need to display them in Site 2. If I specify the site, I get the entries.

{% set entries = craft.entries.section('cenicNews').site('cenicOrg').limit(10) %}

However, they are pointing to Site 1's domain, using Site 1's template:

site1.com/news/new-library-initiative

I want them to be loaded on Site 2's domain, using Site 2's template:

site2.com/news/new-library-initiative

Note, if I remove the site parameter from my query, I get no results.

I figured it out!

In Section settings > Propagation Method, you must choose 'Save entries to all sites enabled for this section', or, if you're using site groups, choose the appropriate group. This basically shares entries across multiple sites.
If your entries were previously only associated with one site, doing this will cause entries to be resaved.
When calling entries, do not specific the site from which they are pulled, in other words, do not include site or siteId in the entries query.

{% set entries = craft.entries.section('sectionName').limit(10) %}

Now your urls will be correct, relative to the current site.

On Site 1: firstSiteDomain.com/news/president-elect-not-sociopath-at-all-billions-rejoice

On Site 1: SecondSiteDomain.com/news/post/president-elect-not-sociopath-at-all-billions-rejoice

Even though the entries, news in this case, may be intended as "tied to" Site 1, you must 'share' them with Site 2 in order for Site 2 to load them and create entry.url correctly.


Error 404 When trying to POST to plugin Controller action Before I dive into my problem, I do want to mention that I did do my due diligence and spent the last two days researching this problem, to no avail, so here it goes:

So I'm getting the following error when trying to load an action method from the controller: Unable to resolve the request "ecaApi/main/doGeocodeRequest". I get this error if I try and navigate directly to it, e.g. domain.com/actions/ecaApi/main/doGeocodeRequest

The AJAX request below also generates a 404.

File Structure:

- craft/plugins
  - ecaapi
    - controllers
      --EcaApi_MainController.php
    --EcaApiPlugin.php

EcaApi_MainController.php

 namespace Craft;

    class EcaApi_MainController extends BaseController
    {
        protected $allowAnonymous = true;

        public function actionDoGeocodeRequest() {
            //.....
            $this->returnJson($result);
        }
    }

JS ajax call to the action method

    var data = {
        address: "{{ event.event_location }}"
    };
    data[csrfTokenName] = csrfTokenValue;
    data['action'] = 'ecaApi/main/doGeocodeRequest';
    $.ajax({
        method: "POST",
        url: "/",
        data: data
    })
        .done(function (data) {
            console.log(data);
            markers[{{ event.id }}] = new google.maps.Marker({
                position: new google.maps.LatLng(data.results[0].geometry.location.lat, data.results[0].geometry.location.lng),
                map: map,
                {% if mapSettings.mapIcon %}
                icon: mapMarkerIcon,
                {% endif %}
                title: "{{ event.title }}"
            });
            var content = '{{ event.title }}';
            var window = new google.maps.InfoWindow({
                content: content
            });
            markers[{{ event.id }}].addListener('click', function () {
                window.open(map, markers[{{ event.id }}]);
            });

        });

Any help would be appreciated, I've been pulling out my hair for days over this.

Ok so thanks to @BradBell's suggestion, I regenerated the skeleton of my plugin and it's controller using pluginfactory.io and it now magically works! Still not sure exactly what the difference was, but it does its job now!


HTTP 500 Error after trying to update to Craft 3.1.1 I just tried to update Craft to 3.1.1 from within the CP. The install failed and I got an Internal Server 500 error. I now can't load either the front or backend of the site, I get a 500 error in both cases. The phperror log shows these errors:

[18-Jan-2019 06:03:54 America/Los_Angeles] PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to allocate 14680096 bytes) in /home/vps/proto.site.com/craft/vendor/composer/composer/src/Composer/Util/RemoteFilesystem.php on line 300
[18-Jan-2019 06:03:54 America/Los_Angeles] PHP Fatal error:  Uncaught yii\base\InvalidRouteException: Unable to resolve the request "templates/render-error". in /home/vps/proto.ibiscycles.com/craft/vendor/yiisoft/yii2/base/Module.php:537
Stack trace:
#0 /home/vps/proto.site.com/craft/vendor/craftcms/cms/src/web/Application.php(284): yii\base\Module->runAction('templates/rende...', Array)
#1 /home/vps/proto.site.com/craft/vendor/yiisoft/yii2/web/ErrorHandler.php(108): craft\web\Application->runAction('templates/rende...')
#2 /home/vps/proto.site.com/craft/vendor/craftcms/cms/src/web/ErrorHandler.php(132): yii\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#3 /home/vps/proto.site.com/craft/vendor/yiisoft/yii2/base/ErrorHandler.php(262): craft\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#4 [internal function]: yii\base\ErrorHandler->handleFatalError()
#5 {main}
  thrown in /home/vps/proto.site.com/craft/vendor/yiisoft/yii2/base/Module.php on line 537

I have successfully updated Craft via the CP over the last couple of months, this is the first time it's failed. I also updated the php.ini memory_limit to 512M (from 256M) and the max_execution_time to 300 (from 30) after the update failed but I still can't view either the front end or CP.

I'm in the middle of migrating a site from Expression Engine to Craft, this is kind of worrying! I've never see a complete failure like this without any indication of what the problem is.

Thanks for any help.

The error message indicates your php.ini file's memory_limit setting is still sitting at 256M (268435456).

Double check you've bumped it up to 512M and you're editing the correct php.ini file for the site.

From there, the quickest way to get the site back up and running is if you have Composer installed from the command line, to nuke your vendor folder and composer.lock file, then run composer update from your project's root to pull in a fresh set of Craft's dependencies.

To be safe, you'll probably also want to restore your database from a pre-update attempt database backup as well (you can't be sure at what point the original fatal error occurred during the update process).


CRAFT_CONFIG_PATH not picked up I haves dev setup of a site that i want separate craft config, db and plugin folders for.

define('CRAFT_CONFIG_PATH', '../../../craftdevconfig/');
define('CRAFT_PLUGINS_PATH', '../../../craftdevplugins/');
define('CRAFT_STORAGE_PATH', '../../../craftdevstorage/'); //'../storage/');
define('CRAFT_TEMPLATES_PATH', '../craft/templates/');

I am trying to debug a plugin in craftdevconfig.

However I am having a nightmare knowing where craft is looking.
For my plugin it seems to get its default configuration from the live craft/plugins folder. But for code changes, seems to change its mind about where the plugin service calls should be found. I can debug it locally as it has IP based service calls to third parties. Slowly going bald.

Any ideas on how to clarify where craft thinks stuff is would be great

A plugin that has config settings can provide the default values for those settings in plugins/pluginHandle/config.php.

Since you've defined CRAFT_PLUGINS_PATH as craftdevplugins, that would be craftdevplugins\pluginHandle\config.php.

End users can set can set custom values in config/pluginHandle.php.  Since you've defined CRAFT_CONFIG_PATH as craftdevconfig, that would be craftdevconfig\pluginHandle.php.


How to prevent cache busting (preferably on a single section)? I have a template that displays 1,100 entries as dots on a zoomable/searchable map. The query is cached and it performs beautifully. As expected, if I edit any of those entries the cache is busted and the next page load lags.

What I prefer to do is have the cache rebuilt once per day via a cron job or some other method.

For starters, I added

'cacheElementQueries' => false,

to my general config. I thought this prevents automatic cache busting, but it does not seem to. If I edit an entry, the cache is still being rebuilt (or so I assume by the page load time).

Any help on what I'm not understanding? Thanks in advance.

cacheElementQueries set to false should cause the behavior you're looking for. i.e. Craft no longer keeps track of element queries inside of the cache tag for use in when deciding when to bust them.

Might also be worth looking at the Cache Flag plugin as that lets you more explicitly define when you'd like caches to be busted.


Get a field's required attribute in frontend form I am trying to get the required attribute of a field in a frontend form. I know this attribute only makes sense in the context of an entry type. Doing this

craft.fields.getFieldByHandle('handle')

returns a FieldModel object which has no context to any entry type. This way I can get the field name and the instructions for example. In Craft we set the required attribute in the entry type editor. So my question is:

How can I get a field's required attribute in the context of a specific entry type?

Edit: My goal is to create a frontend form of an entry type and have every change to the field's required flags in the backend automatically reflected in the frontend form: <input required ...>.

I could not find a way to get a specific field for a model but you can get all and loop through them

<!-- fetch all fields for your entryType -->
{% for field in craft.entries.section('yourSection').type('yourEntryType').first.getFieldLayout().getFields() %}
    <!-- loop them and check the handle if it is your field -->
    {% if field.getField().handle == "yourFieldHandle" %}
        <!-- the required attribute -->
        {% set required = field.required %}
        {{ field.getField().name }}
        {{ field.getField().instructions }}
    {% endif %}
{% endfor %}

Edit: if you really want to have only one field instead of the array I could create php code for you but since many people refuse to use php in craft I presented the "slower" twig method

Edit: There is a required property in both the FieldLayoutFieldModel (stored above in field) and in the FieldModel (field.getField() above). What we want as the required flag of the entry type is the first: field.required. This is what appears to be the little red asterisk in Craft's backend.


Is there any Booking/Reservation system for Craft 3? I couldn't find any booking/reservation system for Craft. Is there any? I found questions from 2014, so things could change.

Maybe there is community-approved booking/reservation service that is commonly used with Craft, but it doesn't have the native plugin?

Or maybe it can be done within Craft core? I'm open to ideas.

If you saw a Craft project with booking/reservations feel free to comment it here

Whilst one could build a booking system within Craft either with judicious use of Twig, Channels, and Entries or with a custom plugin it doesn't make a huge amount of sense to reinvent the wheel. Especially when booking management systems are deceptively complex when you get past the initial idea.

It would be possible to integrate pretty much any third party booking system into Craft with a bit of custom plugin development.


Issues with Redactor on Local Install I loaded redactor. When I try to run the install command
./craft install/plugin redactor
I get an error back that says craft is a directory.

It appears the plugin may already be installed.
But I do not know the next step to access it.
I thought I might have a Redactor field type available to select but I do not.

Also the plugin store does not load on my local install.
It says plugin store not available at this time.

Under settings > Plugins nothing shows.

Disregard, I loaded redactor in the wrong directory!


How to get latest entries by particular field? I have 4 fields in my section:

-Images

-Videos

-GIFs

-PNGs

On my homepage, I have a section where I have to show 5 latest entries of Images, Videos, GIFs & PNGs. How can I do that ?

I tried:

 {{set entries = craft.entries.section('works').order('fieldname desc').limit(5) }}

But that didn't work. Please Help. Thanks in advance !!

Looks like you're trying to echo out the results using {{ instead of actually setting it using %} tags.

{% set entries = craft.entries.section('works').order('fieldname desc').limit(5) %}

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

This should fix your problem.


A block must start with a tag name I have that simple variable in my javascript

var toLoad = '{% "include _views/reviews.twig" %}';

and on a page load I get template error that is defined in the title of question.

That variable is defined in template, that includes another template and in that included template is called some 'class' to load reviews.twig from our variable defined in parent template.

Any thoughts?

You've got a quote in the wrong place.

This...

{% "include _views/reviews.twig" %}

should be this...

{% include "_views/reviews.twig" %}


Time to search query Is it possible to render the search time after a query (out-of-the-box)?

eg: 2 results(.0271 seconds)

There are a number of plugins available but I would like to find out what's possible within Craft's core.

You can easily measure the time your request takes using JS.

See this Stack Overflow question for example code: https://stackoverflow.com/questions/3498503/find-out-how-long-an-ajax-request-took-to-complete

var start_time = new Date().getTime();

jQuery.get('your-url', data,
    function(data, status, xhr) {
        var request_time = new Date().getTime() - start_time;
    }
);


Delete entry assets? Is there a way to delete an entry's assets when the entry is deleted? We're going to end up with a lot of orphaned assets.

Or is this not a feature because an asset can be shared between entries? Thus requiring me to write a plugin to tie into the onDeleteEntry event (Where and how should you listen for entry delete in plugin?)?

This is how I did it:

In my custom plugin, in its init function, I use the following statement to hook into the before delete event:

craft()->on('elements.onBeforeDeleteElements', array($this, 'beforeDeleteElements'));

In the same custom plugin, in the beforeDeleteElements function mentioned in step 1, I examine the passed in event, and determine if the entry type being deleted is the entry type I am interested in, if so, I use the following function to find the related assets and then delete them:

protected function deleteRelatedImages(EntryModel $entry)
{
    $criteria            = craft()->elements->getCriteria(ElementType::Asset);
    $criteria->relatedTo = $entry;
    $assets              = $criteria->find();

    if (!$assets) {
        return;
    }

    foreach ($assets as $asset) {
        $sourceType = $asset->getSource()->getSourceType();
        $sourceType->deleteFile($asset);
    }
}


How do you return a message if no entries are found? I thought this would be very easy but I'm having a hard time figuring it out.

How do you output a warning/message if there are no entries to loop through?

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

{% if entries|length %}
    {% for entry in entries %}
      {# Do Loop #}
    {% endfor %}
{% else %}

No entries found.

{% endif %}

Or am I just sleepy and approaching this all wrong?

The for tag has an else clause by which you can render a replacement block if no iteration took place because the sequence was empty.

    {% for user in users %}

{{ user.username|e }}

    {% else %}

no user found

    {% endfor %}

reference: http://twig.sensiolabs.org/doc/tags/for.html#the-else-clause


When to use " is same as" and when to use "==" {% if foo is same as('bar') %}

vs.

{% if foo == 'bar' %}

When to use first and when to use second? Does one of them has advantages over another?

Looking at the Twig code, they have this documented for the “same as” test:

  Checks if a variable is the same as another one (=== in PHP).

I won’t re-hash the details of === vs. == in PHP because there is an excellent comparison here: https://stackoverflow.com/questions/80646/how-do-the-php-equality-double-equals-and-identity-triple-equals-comp

The money quote is:

  == compares the values of variables for equality, type casting as necessary. === checks if the two variables are of the same type AND have the same value.

And here’s what the PHP docs have to say:

http://www.php.net/manual/en/language.operators.comparison.php


How can I server Craft transform assets statically (not through Craft/PHP) I’m using Craft as a backend for an iOS app and theres thousands of asset requests.

Having to go through php instead of straight through Apache is causing too much of a bottleneck.

I want to use Craft's transforms, but I want the resulting URL to be statically accessible (directly through apache), and not have to go through Craft at all.

Possible?

This happens automatically if you use asset.getUrl(transform)...


Trouble with Restrict to Single Folder and Upload Location I have a Matrix field with two blocks. Each block has an assets field that I'm trying to set to restrict to uploads to a single folder and setting the upload location to {slug}. Basically what I'm trying to do is set up a main folder for my portfolio. I want to be able to upload images for each entry and have them placed in a folder with the same name as the entry so whenI look in Assets I see something like:

Portfolio
-- project 1 entry name
-- project 2 entry name
...
-- project 17 entry name
-- project 18 entry name

When I upload the file in my entry form no subfolders are created. I have tried a few things to remedy the problem to no avail. I've set permissions to 777 and have tried placing a / before {slug} because the fileSystemPath variable doesn't have a trailing slash in my general.php config file. Any other issues that might cause this?

Since you're inside the context of a Matrix block, you'll want to use:

{owner.slug}

instead of:

{slug}

More information on that http://buildwithcraft.com/docs/assets-fields#dynamic-subfolder-paths


Problem loading css files in some templates I´m having problems loading css files in some templates.

Hi have this to load css files:

{% includeCssFile "assets/html/css/reset.css" %}
{% includeCssFile "assets/html/css/general.css" %}
{% includeCssFile "assets/html/css/typography.css" %}
{% includeCssFile "assets/html/css/layout.css" %}
{% includeCssFile "assets/html/css/formStyle.css" %}
{% includeCssFile "assets/html/rs-plugin/css/settings.css" %}
{% includeCssFile "assets/html/css/slidestyle.css" %}
{{ getHeadHtml() }}

This follows this path: http://localhost/craft/assets/html/css/filename.css

It works great , and load the styles for example, in the news section.
But, when i go to que news detail section, the styles don´t show.

The news detail template extends the template layout used for the home page and other sections.

The page looks for styles in this path: http://localhost/craft/news/assets/html/css/filename.css   and of course don´t find it.

I try with routes ( don´t know exacly how it works , but try and didn´t work ).

Help! :)

Thanks. Sebastian.

You will want to include the siteUrl variable before the file path to normalize all the resource URLs. That will also help when you move to production. For example:

{% includeCssFile siteUrl~'assets/html/css/slidestyle.css' %}

In your general config file, you can set different site URLs based on the server you are on.

return array(
    '*' => array(),

    'localhost => array(
        'devMode' => true,
        'siteUrl' => 'http://localhost/craft/'
    ),
    'yourProductionDomain.com' => array(
        'devMode' => false,
        'siteUrl' => 'http://www.yourProductionDomain.com'
    ),
);


How to display images in templates I am new to Craft CMS and struggling to figure out how to display my logo in my template.

The error message is:

Variable "siteLogo" does not exist

This is the code I am using:

{% for asset in siteLogo.siteLogo %}
    <img width="180px" src="{{ asset.url }}" alt="ICW Logo">
{% endfor %}

Any advice would be greatly appreciated!

Thanks in advance.

Welcome to Craft! Seeing as you are new I will try and explain the error, and offer some advice on how I would architect what you are trying to achieve:

Variable "siteLogo" does not exist.

This error is coming from the code sample you provided, more specifically it is referring to this part:

{% for asset in siteLogo.

Craft is looking for something called siteLogo. It's not looking for a field called siteLogo, rather a place where that field might exist.

I would assume your asset field exists inside a Global? That's how I would do it, but you just need to double check what the handle of your Global is, as that's what you use to access it in the templates. Also double check what the handle of your asset field is, as again, that's what you use in the templates to reference that field and get the asset in it.

So taking that, here is what I would do:

Firstly, I would use a Global, but I wouldn't call this "Site Logo". That is too specific. Maybe call it "Branding", as this opens up the possibility of adding more fields (Colour Scheme etc).

When you create a Global, Craft automatically generates the handle from the name you provide. A Global with a name of "Branding" would have a handle of branding.

Next I would create an Asset field, and call this "Site Logo". This handle would automatically generate to siteLogo (what you have already). You would also want to restrict this Asset field to 1 asset.

Now for the template.

As you only have 1 logo you do not need to do a forloop. Instead you can just output the 1 asset like so:

{% set logo = branding.siteLogo.one %}
{% if logo|length %}
    <img src="{{ logo.url }}">
{% endif %}

I hope that helps!


Relational query get error "Getting unknown property" I built up a plugin and inside the plugin I created the relation between Entry and Section using ActiveRecord, like below:

In SectionRecord :

class SectionRecord extends ActiveRecord
{
    public static function tableName()
    {
        return '{{sections}}';
    }

    public function getEntries()
    {
        return $this->hasMany(EntryRecord::className(),['id' => 'sectionId']);
    }
}

In EntryRecord file:

class EntryRecord extends ActiveRecord
{
    public static function tableName()
    {
        return '{{entries}}';
    }

    public function getSection()
    {
        return $this->hasOne(SectionRecord::className(),['sectionId' => 'id']);
    }
}

At last in controller:

$hub = SectionRecord::findOne(['handle'=>'hub']);
dd($hub->entries);

I tried to get section which is working, but when I wanna fetch entries under that section, showed me an error:

Unknown Property – yii\base\UnknownPropertyException
Getting unknown property: raintestplugin\test\records\SectionRecord::sectionId

Because the property sectionId does not exist in the sections table. All models in Craft have the property id as their primary key not - for example - sectionId


Sub categories with extra field I'm stuck on a problem with categories. I never worked with them before so maybe it's not as difficult a I think.

I need the following structure:

Type of company 1 (e.g. Bakery)
    -Bakery 1 + website
    -Bakery 2 + website
    -Bakery 3 + website

Type of company 2 (e.g. Supermarket)
    -Super 1 + website
    -Super 2 + website
    -Super 3 + website

So my parent categories only need 1 field (type of company)
and the children need 2 types of fields (url + company name)

How can I accomplish this?

I would just leave the URL field blank for parent categories.

In the template you could check if the the category is level two before outputting the URL field.

{% if category.level == 2 and category.categoryUrl %}
    {{ category.categoryUrl }}
{% endif %}


Override "sameAddress" option when updating cart I'm working on a new checkout process which involves taking the shipping address first.

To do this I am setting sameAddress to 1 in my shippingAddress form.

Then, at a later stage I want to offer the customer the option to change their billing address. I thought I could just present the customer with a new form for billingAddress fields and set sameAddress to 0, or false.

However, this doesn't seem to let me override the already stored billing address. It seems that once sameAddress is set, it cannot be unset.

I have tried setting a hidden billing address when setting the shipping address but again it's not letting me override that later.

Is there way I can achieve this checkout flow?

Shipping address info -> shipping method selection -> payment (with option to use a different address for billing)?

It is possible to override sameAddress

The trick to doing this is as follows:

Checkout Step 1: Set your shipping address and use sameAddress1

<form method="POST">
    <input type="hidden" name="action" value="commerce/cart/updateCart">
    <input type="hidden" name="redirect" value="/checkout/shipping">
    <input type="hidden" name="shippingAddressId" value="new">
    <input type="hidden" name="sameAddress" value="1">

      shipping address fields here

</form>

Checkout Step 2: Set shipping method as normal

Checkout Step 3: Set the billing address, including the shipping address as hidden fields, set sameAddress to 0.

<form method="POST" id="paymentMethod">
    <input type="hidden" name="action" value="commerce/cart/updateCart">
    <input type="hidden" name="redirect" value="/checkout/payment">

    <!-- billing fields -->
    <input type="text" name="billingAddress[firstName]" value="{{ cart.billingAddress ? cart.billingAddress.firstName }}">
    <input type="text" name="billingAddress[lastName]" value="{{ cart.billingAddress ? cart.billingAddress.lastName }}">

    etc.
    <!-- tell Craft this is a new billing address -->
    <input type="hidden" name="billingAddressId" value="new">

    <!-- hidden shipping address fields -->
    <input type="hidden" name="shippingAddress[firstName]" value="{{ cart.shippingAddress.firstName }}">
    <input type="hidden" name="shippingAddress[lastName]" value="{{ cart.shippingAddress.lastName }}">

    etc.
</form>


Force language segment in URL on multi-lingual site I'm setting up a multi-lingual site and want to force the language segments:

domain.com/en/
domain.com/fr/

However if you go in the URL bar and remove the en/ or fr/ it loads up the English page without the language segment. How do I force this to load. Have tried a few .htaccess suggestions found via Google but nothing is working.

Make sure to remove Craft’s default rewrite rule (“Send would-be 404 requests to Craft”) from the .htaccess in your document root, so it’s only in the .htaccess files of your locales’ subfolders. You can now remove the index.php as well and requests to your homepage domain.com/ should now respond with a 404 HTTP error.

You have now removed all (duplicate) routes starting at root level.

Last step is to redirect visits to the homepage to en/ via the .htaccess in your document root.

RewriteEngine On
RewriteRule ^$ /en [R=301,L]


Creating a guest entry, with a Matrix field, using Ajax I'm trying to submit a guest entry by building a javascript array of objects to mimic a form submission and submit using ajax. I've managed to successfully submit the entry with just a title but when trying to add Matrix/Super Table rows I'm having no luck.

I've looked here https://github.com/engram-design/SuperTable/wiki/Updating-a-Super-Table-field-from-a-front-end-form to get ideas for how this should work.

Super table block handle is answers and the Super Table field I'm trying to submit is a plain text field with the handle question.

My current code is:

$('#submitQuiz').on('click', function (e) {
  e.preventDefault();

  var testEntrySubmission = [
    {
      name: 'action',
      value: 'guestEntries/saveEntry'
    },
    {
      name: 'sectionId',
      value: '5'
    },
    {
      name: 'enabled',
      value: '1'
    },
    {
      name: 'redirect',
      value: '/'
    },
    {
      name: 'title',
      value: 'user name'
    },
    {
      name: 'answers[new1][fields][question]',
      value: 'fingers crossed'
    }
  ];

  $.post('http://mywebsite.dev/', testEntrySubmission, function(response) {
    if (response.success) {
      console.log('success')
    } else {
      // response.error will be an object containing any validation errors that occurred, indexed by field name
      // e.g. response.error.fromName => ['From Name is required']
      alert('An error occurred. Please try again.');
    }
  });
});

Answer to my question. I ended up making a form and printing out the post as JSON.

$('#submitQuiz').on('click', function (e) {
  e.preventDefault();

  var testEntrySubmission = [
    {
      name: 'action',
      value: 'guestEntries/saveEntry'
    },
    {
      name: 'sectionId',
      value: '5'
    },
    {
      name: 'enabled',
      value: '1'
    },
    {
      name: 'redirect',
      value: '/'
    },
    {
      name: 'title',
      value: 'user name'
    },
    {
    'name':'fields[answers]',
    'value':''
    },
    {
      'name':'fields[answers][new1][type]',
      'value':'6'
    },
    {
      'name':'fields[answers][new1][enabled]',
      'value':'1'
    },
    {
      'name':'fields[answers][new1][fields][question]',
      'value':'John Smith'
    },
  ];

  $.post('http://mywebsite.dev/', testEntrySubmission, function(response) {
    if (response.success) {
      console.log('success')
    } else {
      // response.error will be an object containing any validation errors that occurred, indexed by field name
      // e.g. response.error.fromName => ['From Name is required']
      alert('An error occurred. Please try again.');
    }
  });
});


Removing the Save button based on some condition I want to remove the Create button of an entry based on some condition (review states) for some users.
Is there a way to hook in within a module?

I would try to do this via JS, using https://plugins.craftcms.com/cp-js
If the information, whether you want to show the button or not, is not available in the entry's edit page itself, create a controller within a module and request the information from the controller via AJAX.


Extend order methods to sort numbers naturally I've encountered a similar problem as in this post: How to order numbers naturally?

I have some products I need sorted on a page by model number, and currently they sort as ABC-1, ABC-12, ABC-2, etc. instead of ABC-1, ABC-2, ABC-12. Is it possible to extend the existing methods or create a custom sort method to use with the order parameter, rather than using a twig filter as suggested in the link above?

It's possible to pass MySQL functions to the order parameter. Using the MySQL LENGTH function, I believe the following should do the trick (assuming your "model number" field's handle is modelNumber):

{% set entries = craft.entries.section('yourSection').order('LENGTH(modelNumber) asc, modelNumber asc') %}


How do Twig filters interact with concatenated strings and shorthand "if-else" ( `var ? true : false` ) strings? If I am outputting content like this:
{{ entry.maxWidth|length ? entry.maxWidth ~ 'px' : 'unset' }}

...and I want to apply the |raw filter to the output (nevermind if it makes sense in this case; it's just an example), where do I put it?
Do I go overboard, applying the desired filter to each bit of string, like this?
{{ entry.maxWidth|length ? 'max-width: '|raw ~ entry.maxWidth ~ 'px;'|raw : 'max-width: unset;'|raw }}

Is it applied after any string concatenation (~) is done?
{{ entry.maxWidth|length ? 'max-width: ' ~ entry.maxWidth ~ 'px;'|raw : 'max-width: unset;'|raw }}

Or is once per set of double-brackets ({{ ... }}) sufficient?
{{ entry.maxWidth|length ? 'max-width: '~ entry.maxWidth ~ 'px;' : 'max-width: unset;'|raw }}

You are asking about Twig's operator precedence:

The operator precedence is as follows, with the lowest-precedence operators listed first: ?: (ternary operator), b-and, b-xor, b-or, or, and, ==, !=, <=>, <, >, >=, <=, in, matches, starts with, ends with, .., +, -, ~, *, /, //, %, is (tests), **, ??, | (filters), [], and .:

| is higher precedence than ~, so 'this' ~ 'that' | raw will evaluate as 'this' ~ ('that' | raw). If you want ('this' ~ 'that') | raw then () is the way to go.
Personally, I would use () to disambiguate in both cases.


How to accept incoming webhooks? I've tested the webhooks plugin. Awesome. Works brilliantly.

How how about incoming webhooks? What if my 3rd party ERP system has changes that it wants to put into my Craft site. How do I go about getting the changes into Craft?

This is nitech from the future. You need to read up a bit. While you built RC Planes from FliteTest with your kids, I went ahead and read up a bit for you:

Webhooks is a concept, not a technology

Webhooks are to apps what push notifications are to us. Remember when you had to check your email to know if there was a new mail? Not any more. Technically speaking, webhooks are "user-defined callbacks made with HTTP". They are usually triggered by some event happening in the source app.

Nitech, you're familiar with HTTP-requests, right? It's kinda the way the web it built. A webhook is simply a POST request typically containing JSON, like this one, coming from your ERP-system:

{
  "sku" : "JU40000365",
  "price" : "29999",
  "stock" : "9"
}

It says: Product with SKU JU40000365 has changes - and they're included in the JSON.

The power of Webhooks is that they're triggered by the source app

Okay, I made that one up. But that's my understanding anyhow. The source detects a change - through whatever mechanism (event, polling, whatever), and triggers a webhook, if there is a webhook configured for that change.

It's the way PayPal tells your shop that the purchase of order 123 was successfully paid - or declined.

How to accept incoming webhooks

So, back to your question - atm. you have to create a plugin or module for Craft CMS in order to handle incoming Webhooks.

If you have a look at Rias' Stripe Webhook Plugin for Craft, you can easily build your own plugin for your specific purpose. Look specifically at the controller.

Also have a look at the Craft 3 documentation, How to build a plugin, which gives you what you need to scaffold and load a plugin into your Craft CMS repo.

I used pluginfactory.io to create a scaffold for the plugin.

In finishing, you may ask why I can't simply just give you a zip-file with the finished plugin. My answer is that you'll be better off experimenting and learning this yourself.


Image transforms return cpresources URL in Craft Pro 2.5.2754 Despite having tried the tips from Image transforms returning cpresources url, I am still having issues with image-transforms.

I am running Craft Pro 2.5.2754

asset-source details as follows:

Handle: paragraphPictures
Type: Local folder
Path: ./uploads/images/paragraphPictures/
URL: /uploads/images/paragraphPictures/

image-transform details as follows:

Handle: frontpageNewsPicture
Mode: Crop
Crop Position: Center Center
Width: 492
Height: 348
Quality: Auto
Format: png

'generateTransformsBeforePageLoad' => true is set in craft/config/general.php

folder-permissions as follows:

public/images including all sub-folders and files are owned by apache:apache (the user that runs the apache service) with 777 rights

template-example

{% if entries|length %}

        {% for entry in entries %}
            {% set entryUrl = entry.url %}
            {% set picture = entry.pictures.first() %}

                        {% if picture %}
                        {% set picData = craft.norsok.getPicData(picture) %}
                        {% set maxWidth = picture.getWidth('frontpageNewsPicture') %}

                    {% endif %}

                        {{ entry.title }}

                        {{ entry.ingress }}

        {% endfor %}

{% endif %}

this is what the page-source looks like

and this is what my console tells me

GET http://norsok.tibe.no/cpresources/transforms/2?x=bptVEjjgx 500 (Internal Server Error)
GET http://norsok.tibe.no/cpresources/transforms/1?x=bptVEjjgx 500 (Internal Server Error)

the actual images are located here:

http://norsok.tibe.no/uploads/images/paragraphPictures/img-9889-kraftfor-1-foto-anita-land.jpg
http://norsok.tibe.no/uploads/images/paragraphPictures/StengelnematodeG.Taksdal.jpg

At this point I am completely baffled and pray to the Stack Exchange-gods for assistance :)

Turns out this had to do with imagemagick (or one of its subpackages) not being compiled in a way that Craft 2.5 thinks fondly of, which we discovered after editing the bootstrap to turn off all custom error-logging.


How can I output a list of categories that are not empty? In other words, categories must have at least one enabled entry assigned to them.

To get all categories that are related to an entry you would do this:

{% set allEntries = craft.entries.limit(null) %}
{% set categoriesInUse = craft.categories.relatedTo(allEntries) %}

If you want to narrow down to a specific category group or any other category property you can do it like so:

{% set newsCategories = craft.categories.group('newsCategories').limit(null) %}
{% set entriesWithNewsCategories = craft.entries.relatedTo(allCategories).limit(null) %}
{% set newsCategoriesInUse = craft.categories.relatedTo(entriesWithNewsCategories) %}

And yes, you probably want to wrap it together with the loop to print the categories in cache tags!


Redactor II image resizing? What is the preferred image handling method when using the WYSIWYG editor? Are inline transforms the only way to prevent large images, or are there any other solutions?

Although I am aware of offering more control by "bodybuilding" the front end, this question is specifically related to the WYSIWYG editor.

Quoting Brad Bell in a recent thread:

  If you're on Craft 2.6.2950 or greater, it includes Redactor II 1.3.1,
  where they re-introduced image resizing and positioning support.

  You'll just need to add:

  imageResizable: true, imagePosition: true to whatever
  craft/config/redactor config file your Rich Text field is set to use
  under its settings.

This combined with the mentioned 3rd party plugins creates a bullet proof and front-end friendly setup.


Craft 3 install problem on windows 10 Anyone come across this problem with installing craft3 on windows with MAMP Pro.

I have installed composer and this seems to be working fine. The php path I added directs composer to C:/MAMP/bin/php/php7.1.20.

I can go through the install process but after clicking finish setup it gives me an install fail screen and says to check my logs.

In the MAMP php logs I get:

  PHP Warning: Module 'curl' already loaded in Unknown on line 0

  PHP Warning: Module 'openssl' already loaded in Unknown on line 0

I've tried unistalling composer and resetting the php path. Did not work.
I also added an ENV variable to the system for PHP pointing to the same path above.

I'm a little stumped with this now. So any help much appreciated.

Cheers

This actually turned out to be a max_execution setting in the php.ini MAMP was using after increasing it from 30 seconds everything worked fine.


How can I order each product variant in my templates? In Craft Commerce is it possible to order each product variant in the template, I would like to order by price ascending, I've tried the below but nothing is returned?

{% for purchasable in product.variants.order('price asc') %}

Below works but displays in order they were added:

{% for purchasable in product.variants %}

product.variants just returns an actual array of the variants, not an ElementCriteriaModel. So you can’t chain on additional params like order.

To get variants using custom parameters, you can do this:

{% set variants = craft.commerce.variants({
    product: product,
    order: 'price asc'
}) %}

{% for purchasable in variants %}
    // ...
{% endfor %}


Solspace Freeform plugin: AJAX without jQuery – how? I am trying to post data using AJAX.

The docs describes a method here. But that is mainly for jQuery.

I have also looked at this url, which also uses jQuery.

But from what I understand, the "action" parameter in <input type="hidden" name="action" value="freeform/api/form">is where the data is sent. But if I send data to that URL, I get a 404.

How do I properly submit a form using AJAX?

From the action you are currently posting to a controller see here

The example shows that in your case it would be looking for a plugin called "Freeform" with a controller named "api" and then within that controller there would need to be a "form" action.  If this isn't there then the page will return a 404 or error as it cannot find the route.

You may be able to create the same thing without using jQuery with something like this method


How do I fetch all categories under a category group using the Element API? I am not too familiar with PHP and am just learning the Element API.
How do I turn this craft query into an element api query?

craft.categories.group("brands").level(1).all(),

In categories this is what it looks like

Brands
    -Luxury
        -Gucci
        -Chanel
        -Louis Vuitton
    -Streetwear
        -Supreme
        -Bape
        -Off White
OtherCategory
    - ...
SomeOtherCategory
    - ...
    - ...

UPDATE
Figured it out based on another answer from ElementAPI handling CategoryModel and returning a heirachical object

I wrote the query like this:

'api/categories/<entryCat:[\w-]+>' => function ($entryCat) {
  return array_merge(apiConfig(), [
      'elementType' => Category::class, 'criteria' => ['group' => $entryCat, 'level' => 1, 'with' => ['children.children.children']],
      'transformer' => function (Category $category) {
        $data = ['title' => $category->title];
        $children = $category->getChildren();
        if ($children) {
            $data['children'] = [];
            foreach ($children as $child) {
              $data['children'][] = ['title' => $child->title];
            }
        }
        return $data;
      },
  ]);
},

where apiConfig() is just common settings such as pg and paginate


Is it possible to access users' search query data? Is it possible to obtain data on what searches have been performed through the front end of a Craft website?

e.g. If there is a search results page which runs

{% set results = craft.entries.search(query) %}

is that the query data stored anywhere? It would be helpful in terms of understanding what kinds of search terms / keywords people are using to search for information.

You could use tracking tools like Google Analytics for this? Otherwise create a plugin that creates entries that keep track of whatever query were used.

https://craftcms.com/docs/plugins/working-with-elements.


Previous/Next button to next item in Matrix I've made a projects structure with inside a couple of projects. On the homepage people can fill in a matrix with either a project or a textblock.

I'm capable of looping through the detail pages in the order that they've been made in the Projects section. I can't however make them loop in the order the client orders his Matrix. I'm capable of finding the current detail page I'm on by using relatedTo(entry) however I'm uncapable of using getPrev / getNext.

{% set source = craft.entries.id(2).first().projectList.type('project').entries.relatedTo(entry) %}

{% for collection in source %}

{% for project in collection.projectEntry %}

    <p>{{project}}</p>

{% endfor %}

{% endfor %}

This returns the current project I'm visiting. Even when using getPrev(entry) or getPrev(source) in there it keeps on returning my current project instead of the prev/next

In order to get previous or next elements in Craft you have to pass the current criteria in the elements getPrev function. In your case

{% set criteria  = craft.entries.id(2).first().projectList.type('project')

{% set prev = collection.getPrev(criteria)  %}


301 redirects not working I'm moving a site from EE to Craft. All is well but the redirects from the old URLs to the new do not work.

I have tried:

SproutSEO Redirects
(https://sprout.barrelstrengthdesign.com/craft-plugins/seo/docs/getting-started/redirects) - Results in a Craft 404
Retour (https://github.com/nystudio107/retour) - Results in a Craft
404
Reroute (https://github.com/davist11/craft-reroute) - Results in a
Craft 404
.htaccess redirect - Results in Craft 404

The only thing I can think of is that Craft is somehow getting ahead of these solutions and running with the 404.

Any assistance would be appreciated.

I wrote the Retour plugin; it will definitely work. However it only kicks in after 404 error is thrown by Craft. So if some route resolves to the URL in question, it won't kick in (because, by definition, it is then not a 404)


Reorder selection of categories in category fieldtype I'm looking to reorder my selected categories within a category field. I'm hoping this is possible because the order an editor selects these categories is important, primarily for the .first() category.

This functionality is currently possible using the entries fieldtype:

But not for the categories fieldtype:

Edit: The order is important here because this field determines where a given piece of content shows up on the site. Our main navigation is defined by these categories.

For instance, if a piece of content is tagged with "Lifelong Health" and "Health Conditions", but the content editor only wants it to show up on the "Lifelong Health" page.

We want an editor to be able to specify multiple categories however, because we have some robust related category action going on, and we have plans on adding in some personalization in a later phase. Having these robust relationships is vital.

In the interim, we've created a second field "Secondary Categories" that an editor can select, and limited the "Category" field to only one selection; but ultimately we'd like to be able to reorder the categories in one field.

Based on Brandon's answer here, this is currently not a feature of Craft CMS (as of v3.0.34).

If you need this functionality, I'd look into doing one of the following:

As mentioned in the comments and in Brandon's answer (linked above), create a matrix field with a block containing a category field that is limited to one category. This will allow you to add multiple categories but be able to rearrange the matrix blocks as needed (thereby changing the order of the categories).
Build your own plugin to achieve this functionality. Also check the current list of available plugins, as it's possible someone else has already created a plugin to achieve this.
Open an issue on the Craft CMS repo (based on Brandon's answer though, seems like P&T don't really think selected categories need to get reordered, so not sure how far you'll get with this unless P&T have changed their minds).


Getting Price and Add to Cart Button. Any thoughts on what I am doing wrong I am trying to get the price and add to cart link for a product

This is what I am testing at the moment, is it completely wrong?

========== Setting Variables ===========

{% set giftDepositID = 6901 %}
{% set giftDepositPrices = 0 %}

{% if craft.commerce.products.id(giftDepositID)|length %}
    {% set giftDepositproduct = craft.commerce.products.id(giftDepositID).find() %}
    {% set giftDepositvariant = giftDepositproduct.defaultVariant %}
    {% set giftDepositpurchasableId = giftDepositvariant.purchasableId %}
    {% set giftDepositPrices = craft.childsown.getVariantPrice(giftDepositpurchasableId) %}
{%endif%}

========== Example HTML ===========

{{"Deposit"|t}}

${{giftDepositPrices}}

                        <form method="POST">
                            <input type="hidden" name="action" value="commerce/cart/updateCart">
                            <input type="hidden" name="redirect" value="commerce/cart">
                            <input type="hidden" name="qty" value="1">
                            <input type="hidden" name="purchasableId" value="{{giftDepositpurchasableId}}">
                            <button class="form-control btn btn-yellow text-white" type="submit" data-click=".softie-form .red .btn">{{"BUY NOW"|t}} </button>
                        </form>

Any help/guidance is appreciated.

======= EDIT ========

New Variable set, I have updated the following:

{% set giftDepositID = 6901 %}
{% set giftDepositPrices = 0 %}
{% set giftStandardID = 6899 %}
{% set giftStandardPrices = 0 %}
{% set giftLargeID = 6897 %}
{% set giftLargePrices = 0 %}

{% if craft.commerce.products.id(giftDepositID)|length %}
    {% set giftDepositproduct = craft.commerce.products.id(giftDepositID).find() %}
    {% set giftDepositvariant = giftDepositproduct.defaultVariant %}
    {% set giftDepositpurchasableId = giftDepositvariant.purchasableId %}
    {% set giftDepositPrices = giftDepositvariant.getVariantPrice(giftDepositpurchasableId) %}
{%endif%}

This is the output of the HTML

Deposit

$

<form method="POST">
<input type="hidden" name="action" value="commerce/cart/updateCart">
<input type="hidden" name="redirect" value="commerce/cart">
<input type="hidden" name="qty" value="1">
<input type="hidden" name="purchasableId" value="">
<button class="form-control btn btn-red text-white" type="submit" data-click=".softie-form .red .btn">BUY NOW </button>
</form>

I am a little confused with your twig code assigning so many variables.

Put simply, only a purchasableId can be used to add something to the cart.

If you know the product ID then it is as simple as:

{% set product = craft.commerce.products.id(6901}.first() %}

{% if product %}
    <form method="POST">
        <input type="hidden" name="action" value="commerce/cart/updateCart">
        <input type="hidden" name="redirect" value="commerce/cart">
        <input type="hidden" name="qty" value="1">
        <input type="hidden" name="purchasableId" value="{{ product.defaultVariant.purchasableId }}">

        <button type="submit">BUY NOW {{ product.defaultPrice|commerceCurrency }}</button>
    </form>
{% endif %}


Default Formatting Templates don't add "required" to input tag I've just started using FreeForm and so far it's fantastic after moving away from another solution.
One thing I'm struggling with is how the "required" option works for fields. I would have expected it to add a required attribute to the input field, but all it does is add a required class to the label of that input.
I know I can add the required attribute myself and that's what I'm doing now but wanted to check I'm not doing something wrong? The formatting template I'm using is Bootstrap 4 and I can see it only uses required in relation to labels.

By default it doesn't add them. You can get that added automatically by adding useRequiredAttribute: true to your form embed tag: https://docs.solspace.com/craft/freeform/v3/template-objects/field.html#properties


Get PDF file from Assets Folder - Element API I have a PDF folder within assets and having trouble accessing the file path once chosen by the user.

To get something like an image from assets within a matrix I do something like this which works:

'image' => $block->image[0]->url,

I have tried similar code however it seems url is not available?

'downloadablePDF' => $block->downloadablePdf[0]

I need to return the URL so that I can pass the URL to a React Component to make the pdf downloadable.

Here is my full code to fetch data from a matrix block:

function articles($array) {
    $articles = [];
    foreach ($array as $block) {
        switch ($block->type->handle) {
            case 'article':
                $articles[] = [
                    'heading' => (string) $block->heading,
                    'subheading' => $block->subheading,
                    'text' => (string) $block->text,
                    'backgroundImage' => (isset($block->backgroundImage)) ? $block->backgroundImage[0]->url : '',
                    'downloadablePDF' => $block->downloadablePdf[0],
                    'videoUrl' => (isset($block->videoUrl)) ? $block->videoUrl : '',
                    'linkUrl' => (isset($block->linkUrl)) ? $block->linkUrl : '',
                    'size' => (isset($block->size)) ? $block->size->value : '',
                    'style' => (isset($block->style)) ? $block->style->value : '',
                    'verticalTextAlignment' => (isset($block->verticalTextAlignment)) ? $block->verticalTextAlignment->value : '',
                ];
            break;
        }
    }
    return $articles;
}

You have to check if your matrix element has a block or not with the code $block->downloadablePdf[0]->url you'll access the first element in your matrix but when there is no it will throw an error.

'downloadablePDF' => $block->downloadablePdf[0]? $block->downloadablePdf[0]->getUlr() : ''

will check if there is an asset and return the url or an empty string if there is no asset


Integrating Yii 2 modules and frontend widgets with Craft CMS 3 I'm experienced in Yii2 framework, but I just discovered Craft CMS and I already love it.

I use to develop Yii 2 modules using their own DB tables, models, controllers and views.
I usually use Yii extensions and widgets from Krajee (http://demos.krajee.com/widgets) for the frontend, to create rather complex forms.

I'd like to integrate such modules and widgets in some pages of a Craft-managed website, using Craft for user management/access control and templating if possible.

Which structure would you recommend ?

Should I keep a separate instance of Yii 2 on the same server ?  If so, how can I integrate Yii2 views (PHP code) in Twig templates (no PHP) ?

Or should I redevelop Craft modules or plugins using Twig ?  If so, how can I integrate Yii 2 frontend widgets and extensions in Twig templates and Craft CMS modules ?

Thanks in advance for your advice.

If you go down the route of creating your own modules for craft/yii2:

You can register access from twig to PHP services in your modules init() function

Event::on(CraftVariable::class, CraftVariable::EVENT_INIT, function(Event $e) {

        $e->sender->set('phpaccess',services\yourService::class);

    });

Access functions within this service in twig with

{% craft.phpaccess.yourfunctionname() %}

You could also return a twig template from your modules controller with some additional variables

$view = $this->getView();
$html = $view->renderTemplate(
    '/_templatename.twig',
    [
        'additionalvar' => $somephpvar
    ]
);

return $html;

Also if you havn't already seen the pluginfactory utility, it does save a lot of time!


Craft Licensing and Multiple Environments on Different Domains It isn't currently clear how licences work with dev, staging and live environments or to be clearer how it works with different domains.

I have the following domains setup for different environments.

Local: name.test
Staging: sub.domain.co.uk
Preproduction: domain.co.uk
Production: livedomain.com

But this can't be configured on the Craft ID site as far as I'm aware? This also results in Licence Errors across staging and preproduction environments.

Is there a solution to what I'm describing?

There's a guide to how licensing is enforced :)


Query a subselection of Sales and Discounts Imagine a less than perfect world, where a 'bundles' product type is just a new product with a sale/discount on it.
Would there be any way to query all sales and all products only related to these but ignore every other regular "used as intended" sale/discount?

From what I can tell, there is no way to categorise sales/discount, only ways to categorise what the affect?

While this may not be the best solution, here is a working example for anyone else who has a similar problem (pure twig).

The saleContainsOtherProduct variable is an indication that the sale did NOT match the criteria of only related to product type so in order to check for only related, use not saleContainsOtherProduct.
Additionally, this only lists sales, but the code within the loop will work for discounts too, so just repeat with a for loop on craft.commerce.discounts.getAllDiscounts()

{% set ignoredProductTypes = ['bundles'] %}
{% for sale in craft.commerce.sales.getAllSales() %}
    {% set saleContainsOtherProduct = false %}
    {% set purchasables = [] %}
    {% for id in sale.purchasableIds %}
        {% set purchasable = craft.commerce.purchasables.getPurchasableById(id) %}
        {% if purchasable.product.enabled %}
            {% set purchasables = purchasables | merge([purchasable]) %}
        {% endif %}
    {% endfor %}
    {% for id in sale.categoryIds %}
        {% set category = craft.categories.id(id).one() %}
        {% for product in craft.products().relatedTo(category).all() %}
            {% if product.enabled %}
                {% for purchasable in product.variants %}
                    {% set purchasables = purchasables | merge([purchasable]) %}
                {% endfor %}
            {% endif %}
        {% endfor %}
    {% endfor %}
    {% for purchasable in purchasables if purchasable.product.type.handle not in ignoredProductTypes %}
        {% set saleContainsOtherProduct = true %}
    {% endfor %}
{% endfor %}


When is the find() method actually necessary? I've noticed code samples like the following that use the find() method.

{% for entry in craft.entries.section('blog').limit(5).find() %}

{{ entry.title }}

{% endfor %}

I've noticed that this code sample works just as well without the find() method attached. Is there a particular reason we use find()? Are there instances where it's required? Should I be using it even if the code works without it?

The ElementCriteraModel description in the docs explains what the find() method does.

Essentially, when you use craft.entries you are building an ElementCriteriaModel object that represents the query that you want to make. That query doesn't actually happen until you call the find() method.

As the docs state, as soon as you treat craft.entries (or a variable that you have assigned it to) as an array, by using it in a {% for %} loop, or by calling the | length filter on it, find() is automatically called.

So in the example in your question, you can safely leave out the find() and it will be called automatically anyway when the {% for %} loop is executed.


What is the proper way to fetch items from a custom db table and output them in twig? My main issue is perhaps not totally understanding the unique idioms of Yii, but I need to output data from my custom data tables in a template. So obviously I have a MyPluginVariables class and a MyPluginService to handle to actual business logic.

Using my MyPlugin_FooRecord I can use ActiveRecord to fetch some data. But this obviously returns the actual record objects, when I really just need a model passed to the template. From what I understand, I should use models not the records themselves. Is this correct?

Also, how exactly should I build a chainable syntax, similar to fetching entries in a template?

For example, I am trying to get this to work:

{% set rows = craft.myPlugin.someMethod().limit(10).order('some_col').sort('asc') %}

So relating this syntax back to my original question, how would I build this and return an array of models? The ElementCriteriaModel will automatically execute the find() method when it's used an array, should I just overriding the __toArray() method?

I have been searching and looking through the docs, so perhaps I missed something. I have also been looking through the code in Craft, but due to the abstractions and not being familiar with Yii core classes, I am getting a little lost in figuring out how this works.

I was able to figure almost all of this stuff out by diving through the code. The only exception is triggering the find() method automatically.

The key here was to follow the code in the ElementCriteriaModel. First thing I did was create a method in the MyPluginService class to fetch the data.

public function foo($criteria = false)
{
    return new MyPlugin_MyCriteriaModel($criteria ?: array());
}

I added a wrapper to my MyPluginVariable class.

public function parcels($criteria = array())
{
    return craft()->myPlugin->foo($criteria);
}

This will allow for the following syntax:

{% set data = craft.myPlugin.foo().limit(5).offset(1).find() %}

You must define a find() method in your MyPlugin_MyCriteriaModel class that will perform the SQL query to actually fetch your data. You also have to loop through the results set and instantiate MyPlugin_FooModel for each db row if you want to return a Model to the template instead of a stdObject or array.

For example, my find method looks like this:

public function find($attributes = null)
{
    $this->setAttributes($attributes);

    if (!isset($this->_matchedObjects))
    {
        $objects = craft()->myplugin_foo->find($this);

        $this->setMatchedObjects($objects);
    }

    return $this->_matchedObjects;
}

Then in MyPlugin_FooService I have this:

public function query(MyPlugin_MyCriteriaModel $criteria)
{
    $record = new MyPlugin_FooRecord();

    $query = craft()->db->createCommand()
        ->select('*')
        ->from($record->getTableName())
        ->limit($criteria->limit)
        ->offset($criteria->offset)
        ->order($criteria->order);

    if($criteria->id)
    {
        $query->where('id = :id', array(':id' => $criteria->id));
    }

    if($criteria->uid)
    {
        $query->where('uid = :uid', array(':uid' => $criteria->uid));
    }

    // More conditionals may go here

    return $query;
}

public function find(MyPlugin_MyCriteriaModel $criteria)
{
    $query = $this->query($criteria);

    return $this->_populateModelsFromArray($query->queryAll());
}

protected function _populateModelsFromArray(Array $array)
{
    $models = array();

    foreach($array as $row)
    {
        $models[] = MyPlugin_FooModel::populateModel($row);
    }

    return $models;
}