Matrix field alternative (or easier field grouping) Just started working with Craft, and it's been great! One question I have is regarding the 'correct' way to create a group of fields, rather than using Matrix or Table.

To better illustrate, see the below screenshot of a Matrix, which is setup as a Global for people to add their contact information.

Obviously, it's not really being used for the purpose of repeating content, but at least it groups fields together nicely. It does have the greyed-out button when setting Max Blocks to 1.

The other alternative would be to create each Field separately for the 3 fields above, and add those to the Field Layout. This is kind of a fair amount of work, and leaves you with a massive cluster of Fields (I'm aware you can Group them).

Anyway - hope I've explained myself! Just wanted to bounce ideas around, and at least make sure I'm on the right track!

Depending on the amount of data required (and if it makes logical sense to have the data separate from the entry) I'd probably set this up as a "Contact details" section itself, using the individual matrix fields from your example.

Then if some other entry type needs it, set up an entries field in that entry type, limited to entries in the "contact details" section and limited to the number of contact detail entries you want to show, probably 1 going by your example.

This way you can still have your details grouped (as a separate entry) but it's more compact as a single field rather than a matrix in the fields of the entry you're using the contact details in.

The trade-off is that you need to enter your contact details entry before you try to add it to your "x" entry field.

If it makes no logical sense to have the fields as entirely different sections and your fields are getting cumbersome I think the best way is to put them in a different tab to at least remove the clutter.


Image transforms on remote server are broken by `cpresources/transforms` url My local image transforms successfully load in the FE but they do not load remotely.

This is the local url where images load successfully /uploads/case-studies/deployment-image/_caseStudyDeploymentImage/image.jpg

This is the remote url where images are broken https://www.mysite.com/cpresources/transforms/8?x=IXLDGt3eW

This has happened before but i can’t remember why or how i fixed. i have cleared asset indexes in CP Settings.

1.) Did you check if you have GD extension or ImageMagick extension working on the remote system?

2.) Are you using Apache on your remote system? If yes, is mod_rewrite working? (because the second URL does not point to a real file but is rewritten instead)

3.) Maybe you are using Nginx on the remote system and therefor pretty urls are not working?


What might cause logins to work locally yet not in deployed environment? We're getting a 404 (or a Craft 'page not found' error) with this path on the remote server:

http://foo.example.net/admin/actions/users/login

Same database using remote mySQL. Local logins work fine.

Craft CMS 2.6.2989

Crikey...

/admin/actions/users/login 404

https://twitter.com/nystudio107/status/895726366477344772

Forge!


No Underscores Allowed in Freeform Field Handles? We're trying to use Solspace Freeform with a third party CRM and it (the CRM) has a field name convention that uses underscores (x_firstName, etc). We're trying to set up corresponding forms in Freeform and want to map fields using the same field names as the CRM but it appears that it (Freeform) will not let you use underscores when typing out the handle for a field.

Does anyone have any knowledge of this? It seems an odd convention in Freeform to disallow underscore in its field handles.

Not likely to be an issue for anyone in the future but just incase someone is looking at this... Turns out Freeform should be able to handle underscores in field handles. Just an oversight on the Solspace side and they're going to be patching in support for underscores shortly.


Image Transform of Transparent Images results with Black Background As the title suggests, I am receiving a black background on my transparent PNGs. I'd prefer them to be white. I have enabled imagemagick in the config file but still no dice. I noticed GD library applies a similar black background so I'm thinking it's the process of conversion in Craft. Any suggestions?

See here:

CRAFT
http://staging.munacosealing.com/products/cut-gaskets-custom-gasekts-oem-gaskets-prototype-gaskets-asme-gaskets

I was able to remedy this with a plugin (requires ImageMagick)
https://github.com/aelvan/Imager-Craft#bgcolor-string


Preserve formatting when exporting from Wordpress I'm using the WP All Export plugin to get posts out of Wordpress, and Feed Me to import to Craft 3. Problem I'm having is I can get a great XML feed out of the WP All Export plugin, but post "Content" fields lose formatting so all comes out as one blob – no paragraph tags etc... Anyone lend a hand?

Just needed to save each entry and formatting corrects itself.


InvalidArgumentException with composer I need your help, please.

At https://docs.craftcms.com/v3/installation.html#step-1-download-craft I have this instruction:

  "To create a new Craft project, run this command (substituting  with the path where Composer should create the project): composer create-project craftcms/craft "

I get

  "[InvalidArgumentException] .Could not find package craftcms/users/lestone2 with stability stable"

on entering composer create-project craftcms/Users/lestone2.

I get the same message on entering php composer.phar create-project craftcms/Users/lestone2

Can someone help me to set up the instruction correctly? Or do I have a permissions issue? (I am logged on as the admin.).

I found one related question at: https://stackoverflow.com/questions/36897084/could-not-open-input-file-composer but the issue there is not the same as mine.

Thanks in advance.

P.S. I downloaded v1.7.3 of composer at https://getcomposer.org/download/ , and the file's integrity was confirmed.

You're just entering the command slightly wrong is all. Where you go:

composer create-project craftcms/Users/lestone2

It should be:

composer create-project craftcms/craft /path/to/project

The craftcms/craft bit isn't a directory, it's vendor/package. The path to where you want your project to be (including the name) comes after. Good luck!


extractSummary twig filter is unknown I'm trying to set a fallback description in case there is none. This is the bit of code:

{% if seomaticMeta.seoDescription is empty and entry is defined %}
    {% set fallbackDescription = "" %}
    {% if entry.description is defined %}
        {% set fallbackDescription = extractSummary( entry.description ) %}
    {% endif %}
    {% if fallbackDescription is not empty %}
        {% set seomaticMeta = seomaticMeta | merge({
            seoDescription: fallbackDescription,
            og: {
                description: fallbackDescription
            },
            twitter: {
                description: fallbackDescription
            }
        }) %}
    {% endif %}
{% endif %}

But I get an error: Unknown "extractSummary" function in "_globals/_layout".

Am I doing something wrong?

So it sounds to me like either the plugin isn't installed (which doesn't make a ton of sense, because you'd be getting other errors, too), or if it is installed, pieces of it are missing.

extractSummary is a Twig filter/function and has been available since the start; I'd try making sure there's nothing wrong with your install/deploy, or try re-downloading a fresh copy of the SEOmatic plugin.


Is it bad practice to re-use array variable name? I have this line

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

Which I use to pull data for a header in my _layout.html template.

Then later, knowing I am done with the array, inside a block in an another template which extends _layout I have

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

In fact I have reused the 'entries' name regularly through many templates and a number of sites and haven't, as far as I know, had a problem, except in one case.

In this case if the cache has just been cleared (manually at least) then the second setting of entries does not work and it contains the values from the first set, mucking up the page of course.

On reloading the page, and subsequently until the cache is cleared manually every thing works as expected (although I don't know what happens when the cache automatically clears as I am never sure when that has just happened).

It was an easy fix just to change 'entries' to 'whatevers' in the second line, and where it is referred to in the rest of the template.

Is it bad practice to do what I have done (before the change), or is it likely that there is another error in my code that is causing this problem?

There's no problem re-using a variable name from a technical point of view. There is likely a problem elsewhere in your code.


How to include asset fields in query in php? I am trying to figure out how to return asset field(s) in a PHP entry query. Also if I could learn how to return "custom fields" when returning an Object that would be great too! Right now I am having to specify asArray() to even get access to most of my "custom fields"

As as example: I have a Vehicle Entry which has a custom field with the handle of price (number field) and another custom field (asset field) with the handle of images. When I execute the query without specifying the asArray() param I cannot find the custom fields included in the results. But if I specify asArray() then they are all there with the exception of my images field which I think is because it is an asset field or possible because it can be a collection of images? How can I make sure all the fields tied to an entry are returned in my query?

Here are some examples of queries and the corresponding results:

PHP Query without asArray():

$entry_query = Entry::find()
->section('inventory')
->all();

Returns:

PHP Query results with asArray():

$entry_query = Entry::find()
->section('inventory')
->asArray();

However even when specifing to make the result set an array I still cannot figure out how to include the 'images' field.

The images is a field added to the section (see below)

I am having a difficult time finding an answer via the documentation or an example of someone doing the same. All the examples i find are for the template side in twig.

Thanks!

You can skip the asArray() part, as the object has all you need:

// This bit returns only the query that defines what objects to get
$entryQuery = Entry::find()->section('inventory');

/*
 * This will get you one object, based on the query.
 * Use all() if you want an array of objects
 */
$entry = $entryQuery->one();

// Of course, this could be written as
$entry = Entry::find()->section('inventory')->one();

/*
 * Thanks to Yii2's magic getters and setters,
 * and the way Craft uses them, any custom field can be
 * obtained by using the field's handle:
 */
$customFieldContent = $entry->customFieldHandle;

/*
 * If the custom field is any kind of relation field (assets, categories, tags ...),
 * this custom field attribute would return a new query object.
 * You'd need to call all() or one() again to get the actual related objects:
 */
 $images = $entry->customImagesFieldHandle->all();

/*
 * To improve performance, you can eager load the images:
 */
$entry = Entry::find()
    ->section('inventory')
    ->with(['customImagesFieldHandle'])
    ->one();

/*
 * ... which would result in an array of images as the value of the
 * customImagesFieldHandle attribute
 */
$images = $entry->customImagesFieldHandle;

Some light reading:

About entry queries

About eager loading

Addition, after seeing exactly what it is you want. Use Craft's (or actually Yii's) ArrayHelper map() function to loop through your entries and mapping entry content to what you want:

use craft\helpers\ArrayHelper

// Use the map() function to do what you want
$entries = Entry::find()->section('inventory')->all();

$entriesAsYouWantThem = ArrayHelper::map($entries, 'id', function($entry) {
    return (object)[
        'images' => $entry->images->all(), // or without all, if you use ->with()
        'price' => $entry->price,
        'year' => $entry->year,
        ... All other fields
    ];
});


Pro trial not showing Craft Personal 3.0.0-RC2 doesn't show the Pro Trial offer anymore? Is that correct?
I did install a Pro License however this triggers the system to change the domain (from local dev to live) which is unwanted.

Nothing around trial usage has changed between Craft 2 and Craft 3, so as long as you follow this, it should work:

https://craftcms.com/support/try-craft-client-pro

Just keep in mind (because it sounds like you're swapping license keys around) that license.key info is cached for 24 hours at a time, so be sure and clear craft/storage/runtime/cache in whenever you do something like that to get accurate results and hit the updates page if you need to force a call home to Craft's web services.


How would one allow users to edit only some other users entries but not all? Linked via a related field I have users that are assigned to an organisation. An organisation can have many users.

I would like users within the same organisation to be able edit entries (in this case projects) created by a "parent" user in that organisation.

I could allow users to edit other users entries, and code into the template a check if they are not in the project's organisation then dont allow them to edit it, but this is not fool proof because someone could change the hidden project ID fields in the HTML and therefore could save an entry for another organisation.

Anyone have any ideas how best to accomplish the above?

Thanks

I managed to implement it, don't know if it's the best way. Ideally I would like to disallow users to edit other entries, then in the EVENT_BEFORE_SAVE grant permissions, but I think the permissions are being checked before EVENT_BEFORE_SAVE so I could not find a way to tap into it before then.

Therefore I have granted the user permissions to edit other users entries, then the following code will check if the user is in the same organisation, and if they are not set the event isValid to false, which stops it saving.

Event::on(
Entry::class,
Entry::EVENT_BEFORE_SAVE,
function(Event $event)
{
    // Only do it on front end
    if (Craft::$app->request->isSiteRequest)
    {
        // Get Entry Data
        $entry = $event->sender;

        // Only call it if its sectionId 1 = Projects or 22 = KPIs
        if( $entry->sectionId == 1 || $entry->sectionId == 22)
        {
            // Entry Organisation ID
            $entry_organisation_id = $entry->client->id[0];

            // User Organisation ID
            $user = Craft::$app->getUser()->getIdentity();
            $user_organisation_id = $user->getFieldValue('organisation')->first()->id ;

            if($user_organisation_id != $entry_organisation_id)
            {
                // Disallow!
                $event->isValid = false;
            }

        }
    }
}

);

Code goes into a plugin init() function I have for my site.


Thousands of user registrations advice Bit of a generalist question but in case someone has anything to share... I am thinking of building a campaign site in Craft that will register 15,000+ users. Is there anything I should be aware of or pay particular attention to?

The users will have a front end registration and a dozen custom fields.

I would also think about spam bot registrations, which can be a big problem, and the more popular your site, the more attempts you're likely to see.

I'd add something like Google recaptcha to avoid that...we use this: https://github.com/aberkie/craft-recaptcha - and have had zero spam registrations since launch.

With our old site we used to get them all the time (not Craft but a form is a form, so that doesn't really matter I think!)


Displaying Craft templates in external apps I'm working on a site that is running Craft v2 and also has an installation of Simple Machines Forum (SMF). This has been fine using the approach outlined in this answer, i.e.
$craft = include '/craft/app/bootstrap.php';
echo $craft->templates->render('includes/nav');

Now the site is being upgraded to v3 and this isn't working anymore. I've tried this:
$craft = include '/craft/vendor/craftcms/cms/bootstrap/web.php';
echo Craft::$app->getView()->renderTemplate('includes/nav');

but with that I get:

Uncaught Error: Class 'craft\services\Config' not found in /craft/vendor/craftcms/cms/bootstrap/bootstrap.php on line 165

What else is missing to get this to work for Craft 3?

You should take a look into Craft's index.php file. It sets some constants, autoloads Composer dependencies and loads the environment file before actually loading the script that you tried. Here is the snippet:
<?php
/**
 * Craft web bootstrap file
 */

// Set path constants
define('CRAFT_BASE_PATH', dirname(__DIR__));
define('CRAFT_VENDOR_PATH', CRAFT_BASE_PATH.'/vendor');

// Load Composer's autoloader
require_once CRAFT_VENDOR_PATH.'/autoload.php';

// Load dotenv?
if (class_exists('Dotenv\Dotenv') && file_exists(CRAFT_BASE_PATH.'/.env')) {
    Dotenv\Dotenv::create(CRAFT_BASE_PATH)->load();
}

// Load and run Craft
define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
$app = require CRAFT_VENDOR_PATH.'/craftcms/cms/bootstrap/web.php';
$app->run();


Craft license: what is considered a "dev-sounding subdomain"? On the license enforcement help page, it says there are a number of checks to determine whether or not a domain is public. One of those checks is the following:

Does it have a dev-sounding subdomain (e.g. ‘dev’, ‘local’, ‘loc’, ‘test’, ‘testing’, ‘stage’, or ‘staging’)?

My question is, is there a sure-fire way of determining whether or not a subdomain is dev-sounding? For instance, what if my subdomain has the string 'staging' (e.g. http://staging-subdomain.mysite.com). Or what if it was just an unconventional sounding subdomain (e.g. http://www-next.mysite.com)?

http://staging-subdomain.mysite.com would be considered a dev domain because we look for any of those keywords in the subdomain part of the URL.
http://www-next.mysite.com would be considered a public domain because it does not meet any of the criteria.


unserialize(): Error at offset 0 of 60695 bytes Hey guys I am getting this error:
unserialize(): Error at offset 0 of 60695 bytes
when i push a backup of my local db to my staging environment.
I am using craft 3.
Any ideas? Clearing the /storage/runtime/cache didn't work.

More info here:
The value that php is trying to unserialize() and failing to do so looks like json (The value of info config, see picture below).
Looking at an old version of a db dump, that value looks like it was proper serialized data.
At some point, craft started storing it as json?

Adding a "^" to the craftcms version in the composer.json file and then running composer update, did the trick for me.

for example: "craftcms/cms": "^3.1.22",

I also cleared the cache folder in /storage/runtime/cache


Compress Craft Control Panel CSS and JS per environment Is there a way to compress the Craft CSS for a specific environment?
Currently there is a setting for Javascript, useCompressedJs, set to true by default.

useCompressedJs
Tells Craft whether to use compressed Javascript files whenever possible, to cut down on page load times.
https://craftcms.com/docs/config-settings#useCompressedJs

Is it possible to have a similar option in general.php for CSS?

I don't think there's anything built into Craft. What I do is

{% if craft.config.devMode %}
  <link rel="stylesheet" href="{{craft.config.environmentVariables['assetURL']}}css/style.css">
{% else %}
  <link rel="stylesheet" href="{{craft.config.environmentVariables['assetURL']}}css/style.min.css">
{% endif %}

I use devMode to control whether or not I want the minified css, and I use grunt's cssmin task to build my minified css.


How to store custom data into a user I need some direction in how to store (complex) data into users (best practice)

I'm working on a project for a fitness app. I created a structure with 2 levels for workouts where level 1 entries are skills and level 2 entries progressions for these skills, in the progressions I made a matrix containing a content field for an exercise, a table for data such as sets and reps and some practical information.

Now here is how it should work:

The user starts a workout, a workout contains 4 or more exercises to complete for the workout to be finished. Now when every set of an exercise is done, the user has to fill in a number of repetitions he/she did for every set. When the workout finishes I would like to save all this data in the user (Or should i make a section for this?)

The data looks something like this:

    "skillID": {
      "set1":"rep1"
      "set2":"rep2"
    }, etc

This data is important, the next time this user is logging in and starting a workout I want to retrieve this data to give the user overview of how he/she is progressing, so like comparing the workout data with the user data. Plus it should be some kind of history too. Here it gets a bit to complicated for me, but I want to learn this! My thoughts were this:

Option 1: I create a section, here I create user data entries (so they are related with the user who create them) I store the data somehow on these entries, with tables or something
Option 2: I store 2 JSON models into the user (if possible), 1 for writing and overwriting the progress of each workout (when doing the same workout it overwrites the existing data) and 1 for the history, were every date of doing a workout is a key and the value is the whole workout data provided by the users input.

What is the best way to go?

There are a whole bunch of ways to do what you want to do.

You can add custom fields to user accounts. If you wanted to go with option 2 then you could create a custom text field and write the JSON into that.

Another way would be to add a Matrix field to the user accounts and write a new row for each workout.

And another way would be to create a new channel for Workouts. When a user completes a workout, a new entry is created and related to them. You can then retrieve entries relating to that user to display their workout history.

And yet another way, would be to create a custom model for a Workout and relate it to the user account. Your plugin would then create records using that model and you could retrieve them to display the user's progress.


What is getContent() / the ContentModel, and how do I use it to get and set custom field values? If I want to retrieve content on an element, should I be calling $entry->$fieldHandle or $entry->getContent()->$fieldHandle? What’s the difference?

Likewise, if I’m setting new content on an element, what’s the recommended way to set my new custom field values?

BaseElementModel::getContent() will return the element’s ContentModel. It has two purposes:

To store the element’s data from the craft_content table table when fetching elements from the DB.
To store the POST data that should be stored in the craft_content table when saving an element to the DB.

In both cases, the ContentModel should not actually be accessed directly when getting/setting custom field values.

When you’re getting an element’s existing custom field values, use $entry->getFieldValue($fieldHandle), or simply type $entry->$fieldHandle, since BaseElementModel’s magic getter will act as a wrapper for getFieldValue() if the attribute you’re accessing happens to be a custom field handle.

You should do this rather than go directly to the ContentModel because it gives the field types a chance to prep the raw DB data (via their prepValue() method). Most field types have something they need to do before their data will be ready to be interacted with, and in the case of Matrix/relational fields, who don’t store any data in the craft_content table, it’s literally the only way you’ll have a chance to know anything about the field’s value. For those fields, if you were to call $entry->getContent()->$fieldHandle you would just get null back, no matter how much data the field actually has.
When you’re setting new custom field values on an element, use $entry->setContentFromPost($newValues).

Like getFieldValue()’s relationship to retrieving DB data, this method will give field types a chance to prep the POST data before it gets saved to the database (via their prepValueFromPost() method). Since Matrix/relational fields don’t actually store data in the database, this is actually a moot point here and their post data will end up on the ContentModel exactly how it was passed in to setContentFromPost(), but still, there’s no harm in using that method, and better to get in the habit of using it.

$element->setContentFromPost(array(
    'myEntriesField' => array(1, 2, 3),
));


Upload file size limits to 256MB I have run into a problem of file sizes maxing out on 256MB.

On PHP side I have
upload_max_filesize & post_max_size set to 1GB

I've created a simple PHP script to test upload size and managed to upload 800MB file. So everything points to Craft having some limits.

In Craft CMS config file I have:

'maxUploadFileSize'     => 1073741824

Yet if I try to upload files larger than 256MB I get a modal message saying file size exceeds 256MB.

When determining the maximum upload file size, Craft first takes the php.ini file upload_max_filesize, post_max_size and memory_limit into account by getting the minimum value and only then applying the Craft maxUploadFileSize config setting.


Customise errors when validating model data? I’m trying to set a custom message for a specific model attribute. I have something like this:

protected function defineAttributes() {
    return array(
        'attributeOne' => array(
            AttributeType::String,
            'required' => true,
            'label' => 'Attribute One Label'
        ),
        'attributeTwo' => array(
            AttributeType::String,
            'required' => true,
            'label' => 'Attribute Two Label'
        )
    );
}

How can I customise a specific error message for each attribute so that instead of ‘Attribute One Label cannot be blank’, I get ‘Attribute One Label with my custom message’?

In addition to Brad’s answer (which is what I was looking for), you could also use template-specific messages using conditionals if for some reason you only needed to override the global error in a certain place. Unlikely but here’s an example:

{% if errors.attributeOne is defined ? errors.attributeOne | length %}

Custom message.

{% endif %}


Default to only showing 'Live' entries in CP? I'm finding that the structures view in CP is really slow with the amount of entries I have in it. The vast majority of these will be in draft for some time yet, however. I've had a look through the source of craft.js and hacked it a bit to just show the live entries by default just to see what would happen. The result is the CP is way more responsive in this view but obviously the status button doesn't reflect the right view any more and it's not a good idea to be doing this anyway.

Is there scope to make this configurable via a plugin or something?

I'm adding Brad's comment here as an answer as I think it addressed indirectly this issue:

Craft 2.3 should fix the performance problem of Structures and Categories with large amounts of entries.


How do I redirect to a corresponding route when user switches languages? I'm working on a multilingual site with english and spanish locales. I've built persistent switcher that resides in the site footer. When users click it, it redirects to a corresponding entry in the other locale if one exists and the homepage if one does not.

The problem is that I have a handful of pages on my site that aren't entries: they are routes that are tied to particular templates. For example:

news-events/tags

Is the route for a template that lists all tags in use in my news section, and:

noticias-eventos/tags

would be the corresponding route on the spanish site.

I'd like to be able to check if the current uri is covered by a route and if so, whether there is a corresponding route in the other locale. If so, I'd redirect to that and if not, back to the homepage as per usual.

But from what I can see, routes are not localizable and exist independently of each other. Am I wrong? How would I go about this?

This is a rather hacky workaround, but I have no idea what else you could do to handle it. What I do is to compose an array of "route objects" that basically mirror the routes set up in the CP. This array can then be compared to the URL segments (→ getSegments). Not nice but it works :)

{# Map the corresponding routes #}
{% set routes = [
    { en: 'news-events/tags', es: 'noticias-eventos/tags' },
    { en: 'news-events/categories', es: 'noticias-eventos/categorias' },
] %}

{# Configure and loop through selected site locales #}
{% set locales = ['en', 'es'] %}
{% for locale in locales %}

    {# Is this an entry page? #}
    {% if entry is not defined %}

        {# ... Do entry stuff #}

    {# Not an entry page --> check the routes #}
    {% else %}

        {# Get URL segments #}
        {% set segments = craft.request.getSegments %}

        {# Set var before loop #}
        {% set noRoute = true %}

        {# Loop routes array #}
        {% for prop in routes %}

            {# Check if URL segments match a route #}
            {% if prop[craft.locale]|split('/') == segments %}

                {# Output link to the mapped route #}
                {{ craft.i18n.getLocaleById(locale).name }}

                {# Switch var value #}
                {% set noRoute = false %}

            {% endif %}

        {% endfor %}

        {# No route found --> output a link to the homepage #}
        {% if noRoute %}
            {{ craft.i18n.getLocaleById(locale).name }}
        {% endif %}

    {% endif %}

{% endfor %}


Constructor in custom controller? I'm implementing a custom controller as part of a plugin I'm building. There's some set up work that I'd like to do in the controller's constructor. I created a __construct method, and it seems to work as expected.

My question: Is there some reason that I should call parent::__construct() inside of the constructor? It requires an argument, which looks like it should be an ID of some sort, but I'm not sure where I should get that.

I'm not a huge fan of it but for this situation it might be more appropriate to use init(), Looking at Craft\BaseController:

/**
 * Initializes the controller.  This method is called by the Craft before the controller starts to execute.
 *
 * @throws HttpException
 * @return null
 */
public function init()
{

}

It seems a little odd to me to have a constructor for a controller though, what is it you're doing in it? Yii's Controller filters might be a better fit.


How do I enforce stronger password requirements on my Craft site? Craft’s only password requirements are that it is between 6 and 160 characters long. I want to require my users to enter passwords that are at least 7 characters long, with at least one number and one capital letter.

Is it possible to enforce these stricter requirements, or do we have to wait for this feature request to be accepted?

In Craft 2

Plugins have the power to enforce stricter password requirements using the users.onBeforeSetPassword event.

Add this code to your plugin’s primary class:

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

public function onBeforeSetPassword(Event $e)
{
    $user = $e->params['user'];
    $password = $e->params['password'];

    // Perform our custom validation logic here
    $validates = true;

    if (mb_strlen($password) < 7)
    {
        $validates = false;
    }
    else if (!preg_match('/(\d.*[A-Z]|[A-Z].*\d)/', $password))
    {
        $validates = false;
    }

    if (!$validates)
    {
        // Set $performAction = false so the password doesn't get saved
        $e->performAction = false;

        // Add a custom validation error on the user
        $user->addError('newPassword', 'Sorry but your password must contain an uppercase letter, a number, a haiku, a gang sign, hieroglyph, and the blood of a virgin.');
    }
}

In Craft 3

Craft 3 doesn't have a beforeSetPassword event, but you can pull the same thing off from your plugin's init() method using the User element's User::EVENT_BEFORE_VALIDATE event like so:

public function init()
{
    parent::init();

    Event::on(
        User::class,
        User::EVENT_BEFORE_VALIDATE,
        function (ModelEvent $event) {
            // Check if a new password was set on the user
            if ($event->sender->newPassword) {
                $password = $event->sender->newPassword;
                $errors = [];

                // Perform our custom validation logic here
                $validates = true;

                if (mb_strlen($password) < 7) {
                    $validates = false;
                    $errors[] = 'Too Short!';
                }
                else if (!preg_match('/(\d.*[A-Z]|[A-Z].*\d)/', $password)) {
                    $validates = false;
                    $errors[] = 'Too Simple!';
                }

                $event->isValid = $validates;

                if (!$validates) {
                    /** @var User $user */
                    $user = $event->sender;
                    foreach ($errors as $error) {
                        $user->addError('newPassword', $error);
                    }
                }
            }
        }
    );
}


How to handle mock / placeholder assets in a pattern library? I'm currently building a site with a comprehensive pattern library (along the lines of Lonely Planet's), which Craft seems very well suited to. Each pattern is a partial template and I include it in a page like so:

{% include '_partials/card' with {
  title: 'Something',
  description: 'A bit of text',
  image: 'http://lorempixel.com/400/200/'
} %}

The above, with dummy data, allows me to create an example for the pattern library, but I can then easily reuse the above partial with real data by changing the string values to, eg. entry.title, entry.description, etc. This seems to work pretty well for everything apart from for assets: entry.image is not a string, so there's a type difference with my placeholder image.

So far I've got around this problem with a bit of logic that sets image to entry.image.first().getUrl() if entry.image exists. But it feels like this should really happen inside the partial: firstly it's messy and non-DRY to do this every time / everywhere I include it; secondly, the partial itself should really be able to be responsible for specifying the image transform (if there is one).

So it seems to me there are two possible solutions, but I can't quite figure out how to achieve either. The two options are:

Allow the partial to accept either a string or an asset as its image.
Create a mock asset object in my pattern page that will behave more like an asset if I feed it into the partial, so the partial will work unproblematically when I give it an entry.image.

In the former case, I can't figure out a neat way to handle an input that might be a string or an asset field (Twig doesn't seem to have, eg. an is string test). In the latter case I have no idea how you'd go about it or even whether it's possible.

If anyone has any ideas or experience of how you might achieve the above, I'd be really grateful!

Two ideas come to mind.

First, it would be a lot easier to check if an image exists if you send in null in the include instead of an url to the placeholder image, and then let the cart template know what to do if there isn't an actual image. Something like this:

{% include '_partials/card' with {
  title: 'Something',
  description: 'A bit of text',
  image: null
} %}

And in _partials/card:

{% if image!=null %}

{% else %}

{% endif %}

Second option is to have a dummy image in your Assets library, with a known id, and pass that to the card template. Something like:

{% include '_partials/card' with {
  title: 'Something',
  description: 'A bit of text',
  image: craft.assets({ id: 99 }).first()
} %}

The logic in the card template would be a little bit simpler that way I guess. But maybe you risk that the image is deleted at some point and then everything fails? Depending on the images in your Assets library, maybe you could just pick one at random?

{% include '_partials/card' with {
  title: 'Something',
  description: 'A bit of text',
  image: craft.assets({ order: 'RAND()' }).first()
} %}

Might give some weird results, but.. Depends on your project.


Get query string param that has no value Is it possible to determine whether or not a query string has a param with no value. For example

http://www.test.com?myparam

Technically that's a valid url but the craft.request object doesn't appear to have a way to see that. craft.request.getParam('myparam') returns nothing as does craft.request.getQuery('myparam'). I have tried using an explicit test for 'true' and 'false' but no luck.

Right now I'm grabbing the entire query string value and then using the 'in' to find the param name but thats pretty fragile is another param or value contains the same string.

A parameter with no value set returns an empty string '' for craft.request.getParam(). But if the whole parameter is not included at all, it returns null.

This means you can test for null (→ see Twig docs) like so:

{% if craft.request.getParam('myparam') is not null %}
    My Parameter is included in the query string!
{% endif %}


Freeform Success Notice in Template I am having trouble getting a freeform success message to display. I am posting back to the same template. What is the proper way to check if the form was posted? I see in the freeform render template there is a freeform.hasErrors, is there something for success perhaps?

            {% if craft.request.getPost('firstName')|length %}

Your message has been sent.

            {% else %}

                {{ craft.freeform.form("contactForm", {
                        formAttributes: { "data-abide": true },
                        instructionsBelowField: true,
                        submitClass: "button",
                        returnUrl: "/contact",
                        useRequiredAttribute: true
                    }
                ).render() }}

            {% endif %}

https://solspace.com/craft/freeform/docs

Update: Working code using query param instead. Thanks @Jan_dh.

            {% if craft.request.getQuery('success') == 1 %}

Your message has been sent.

            {% endif %}

            {{ craft.freeform.form("contactForm", {
                    formAttributes: { "data-abide": true },
                    instructionsBelowField: true,
                    submitClass: "button",
                    returnUrl: "/contact?success=1",
                    useRequiredAttribute: true
                }
            ).render() }}

            {% endif %}

You can add a param to your returnUrl in the form composer then check the template for the param.

For example:

You redirect to /contact?success

{% set successParam = craft.request.getParam('success') %}
{% if successParam is not null %}

Hooray! Success!!

{% endif %}

Another option is to Ajax submit your form and just have a modal or something on a succes submit. An example of an Ajax submit can be found in the official freeform docs here.


Different templates for different entry types within a section When specifying the URL and template used to render a section, it's possible to set variable URLs using something like /{postDate.year}/{slug}. Is there any way to accomplish something similar for the entry template, or do all the differences have to be handled within one template that checks for entry type?

Sounds like you're looking for something like this: https://craftcms.com/support/entry-type-templates


Get Commerce 2's Stripe subscription planData as object How do I access a subscription plan's planData? Maybe as an object? I'm using Commerce 2 with the Stripe gateway.

For example:

{% set plans = craft.commerce.getPlans().getAllPlans() %}
{% for plan in plans %}
    {{ dump(plan.planData) }}
{% endfor %}

planData looks like JSON stored as a string. How do I access its data (amount, trial_period_days etc.) on the front end?

Does this need to be handled by a custom plugin? Seems like something that should be available out of the box. Hopefully I just over looked it.

A subscription's planData is currently only stored as JSON. You can use that JSON in your template but you'll need to decode it first. Marion Newlevant’s Twig Perversion plugin adds a json_decode filter among other helpful capabilities to Twig.

With that plugin installed, set planData to a variable with the json_decode filter applied to it then you can access attributes of planData as you normally would in Twig.

{% set plans = craft.commerce.getPlans().getAllPlans() %}
{% for plan in plans %}
    {% set planData = plan.planData|json_decode %}
    {{ planData.plan.amount }}
    {# Stripe stores the amount as cents, so formatting for dollars or euros might look like this #}
    {{ (planData.plan.amount / 100)|currency }}
    {# Trial period  #}
    {{ planData.plan.trial_period_days }}
{% endfor %}


Can't search for drafts in Control Panel I just updated to Craft 3.6x, and I noticed that suddenly all my drafts are showing up by default. This confused users, as there were a bunch of them that people had abandoned. But I noticed that the search filter at the top does not apply to drafts. Is there somewhere to change the setting? And is there also a way to show LIVE entries only, by default?

As per 3.6.5, drafts meta data have become searchable (not custom field) and you can (re)index them by running:
php craft resave/entries --drafts --update-search-indexes

In terms of filtering, you need to manually selete Live to hide drafts from the listing.


How to update (or CRUD) products (or entries) via API? I want to update certain fields of a product (or entry) from a 3rd party.

I see some talk about plugins and some refer to this .all talk about Creating a RESTful API with Craft 3.

I found the Craft CMS Element API, but it seems to be a way to create a read-only API.

How can I update a product's fields from a 3rd party?

Tl;dr: Mutations in GraphQL seems to be the way to go - but GraphQL in Craft doesn't yet support mutations on Commerce elements. As for now, you have to create a Craft CMS Plugin. Follow the progress of mutations support on GitHub
Alternative 1 (sort of)
According to #9667 - the 3rd party CraftQL-plugin lets you do CRUD on everything* via Mutations (GraphQL way of altering data) - so that's an alternative while we wait to the native GraphQL functionality to support Mutations (see https://github.com/craftcms/cms/issues/4835 for status on that one. It's a part of the 4.0 milestone)
Also, Pixel & Tonic has added support for Mutations to the 4.0 milestone for Craft CMS on the built in GraphQL plugin. So it's coming.
* Well, not everything. CraftQL supports the following entities: entries, sites, draft, assets, globals, tags and users. The documentation contains a section about third-party field support that probably lets you implement support for mutating Craft Commerce-fields and other 3rd party fields.
I've added a Feature Request on Craft Commerce regarding GraphQL and mutations. Let's see what they answer.
Alternative 2 (only real alternative in 2019)
Also, if you can't wait for mutations in Craft Commerce, you can create your own Craft Plugin. Have a look at my question "How to accept incoming Webhooks", which helps you along with creating a plugin that can do things like creating and modifying products in Craft.


craft.request.getParam('entryId') returns null on edit page of an entry I have a plugin that's loaded on the edit page of an entry.

Whenever I open the entry edit page, I would like to retrieve the id of this particular entry. I've tried this:
{% set someId = craft.request.getParam('entryId') %}

But this just returns null.

I need the id because I use it in my service: craft.pluginName.getRecordByEntryId(someId).
Which will return information that's bound to that page.

I've also tried this:

{% for entry in craft.entries %}
{% if entry.id %}

EntryId: <input type="text" value="{{ entry.id }}"/>

{% endif %}{% endfor %}

But this will return all the entry id's, which I can't really use.

Any help or advice on how I should tackle this differently?

Thanks in advance!

craft()->request->getParam() is only going to check for the value as part of the query string in the URI or in POST if it is a POST request, which is why it's returning null on the edit entry page in the control panel (it isn't in either).

But since it looks like you're using a template hook in your plugin, you can get the entry id directly from the context:

$entry = $context['entry'];

if (!$entry->id)
{
    // New entry
}
else
{
    $entryId = $entry->id;
}


Search Page with Search Plus I've got SearchPlus working great on my Craft localhost. But what I'm trying to achieve is making it so that when you type in the navigation bar (_layout) from any page it'll jump onto a search results page and display the results there. I've not got a clue how to go about achieving this, any direction or help would be great. My current setup is as follows:

templates/search/index.html

{% block content %}

Basic Search

            <form class="form-inline"
                  id="searchform"
                  data-applicationId="{{ craft.searchPlus.getAlgoliaApplicationId }}"
                  data-searchApiKey="{{ craft.searchPlus.getAlgoliaSearchApiKey }}"
                  data-index="products">

                    Search
                    <input type="search" class="form-control" id="searchinput" placeholder="Search.." autocomplete="off">

            </form>

    {% raw %}

    {% endraw %}
{% endblock %}

{% set searchPlusJs %}
    (function() {
        $(this).searchplus();
    })();
{% endset %}

{% includeJsFile url('//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js') %}
{% includeJsFile resourceUrl('searchplus/js/searchplus-combined.min.js') %}
{% includeJs searchPlusJs %}

This sounds possibly a little awkward, depending on what you may mean by 'jumps onto a search results page'.

Some thoughts towards an answer, as you asked:

actually opening a second page is going to remove you from present context, and raise also the question of getting back if the search results aren't used.
you could arrange something happening on typing in the search box by a javascript event on the typing, likely. Probably some issue with transferring what's already typed into such a page, especially with not missing anything being typed while it happens.
a better plan would likely be a Garnish (Craft's ui framework) or other Javascript scrolling modal coming from the original page, no change in url. This might possibly be arranged so that the typing box stays the same, while Algolia results appear in the modal.

Vuejs might be of use in this modal idea, as it handles events and transfer of watched information (the typing) in its framework.


Using Focuspoint with eager loading I'm using eager loading for retrieving some images for my gallery:

{% set employments = craft.entries({ section: 'employments', with: ['photo] }) %}

This works fine and I can see all images on my page.

I'm using focuspoint (https://github.com/smcyr/Craft-FocusPoint) with imager for resizing the images. I've set a different focuspoint for every image.
But whenever I try to get the focuspoints of a image:
photo[0].focusPctX, all images have the same focuspoints (50).

Whenever I try to retrieve the images without the eager loading function, it works fine.

I've created a new issue @focuspoint github.

Is there someone that had the same problem, or has a solution for this?

After some research I found out that this problem has no simple solution (unfortunately). Check https://github.com/smcyr/Craft-FocusPoint/issues/10.

A solution would be not to use eager loading (obvious). Another solution could be using Craft3 (no plugin needed).

The solution I used was Focal Point (https://github.com/aelvan/FocalPointField-Craft). Focal Point is a tool that can be used with Eager Loading. Focal Point creates a focus point on every asset, that means not on every asset field.


Unable to store Model in Commerce Order Fields I'm trying to use custom order fields in Craft Commerce to store a reference to an object, but all I am getting back is an ElementCriteria model.

I have two custom order fields defined: twoDayOption and fourDayOption. Both are events from Solspace Calendar (Calendar_EventElementType).

In the Commerce -> Orders page, I can set these fields with no problem.

On my product page I have a dropdown for selecting the values of each of these fields. They look pretty much the same. Here is the twoDayOption one:

<select name="fields[twoDayOption]">
    {%- for days in craft.calendar.events({calendar: 'workshops'}) -%}
        <option value="{{ days.id }}">{{ days.title }}</option>
    {%- endfor -%}
</select>

After submitting the form the values for twoDayOption and fourDayOption seem to be lost. When I call {{ dump( cart.twoDayOption) }} I get an object of type Craft\ElementCriteriaModel. It references the Calendar_EventElementType, but the ID that was submitted is not there.

Here's the first part of the output of that dump:

object(Craft\ElementCriteriaModel)#757 (17) {
  ["strictAttributes":protected]=>
  bool(false)
  ["_elementType":"Craft\ElementCriteriaModel":private]=>
  object(Craft\Calendar_EventElementType)#990 (7) {
    ["componentType":protected]=>
    string(11) "ElementType"
    ["_sourcesByContext":"Craft\BaseElementType":private]=>
    NULL
    ["_classHandle":"Craft\BaseComponentType":private]=>
    NULL
[snip]

Looking through Commerce_CraftController, I found that it calls BaseElementModel->setContentFromPost("fields") to set the custom order fields. I added in some logging statements to make sure it was getting the information I expected.

When it enters the method it finds these values from $_POST['fields']:
    Array
    (
        [twoDayOption] => 16
        [fourDayOption] => 12
    )

This is exactly whe I expect. It then finds the two custom field handles that match this. I have other logging statements that show what handle it's processing, and the value it gets from the POST:

2016/09/29 13:54:39 [info] [application] setContentFromPost twoDayOption
2016/09/29 13:54:39 [info] [application] setContentFromPost twoDayOption: 16

setContentFromPost also gets the fieldType, and calls $fieldType->prepValueFromPost. The value 16 is passed in, and 16 is returned.

Finally, that value is put into $this->_content->$handle, and I have a logging statement immediately after that verifies I can retrieve 16 from it. But when I log $this->twoDayOption, I'm getting the ElementCriteriaModel again.

I'm at a loss. It seems to know it's supposed to have a Calendar Event for that field. Should I not be passing in the ID? Is there another step I am missing?

When you assign element IDs to a relation field you need to pass the data in an array format.

See this related answer: In a plugin, how do I save an entry with a related entry

Key information in that answer:

<input type="text" name="fields[customTextField]" value="My Text Data">
<input type="text" name="fields[customRelationField][]" value="101">

Note that in the input field name of 'customRelationField' relations field we are passing an array: fields[customRelationField][].

Also note the relation field data is a full replace. So if you ever want to add an element ID to an existing relation field that has relation already set, you need to get the existing IDs as an array and add your additional IDs when updating the field.


Caching in Craft 3 causes out of memory when including grouped for loop I have a problem with using {% cache %}in Craft 3, although the code runs fine without caching, and it works in Craft 2.

I have narrowed the problem down to the following code:

{% cache %}
  {% set upcomingShows = craft.entries.section('shows').orderBy('showTime asc').limit(null).all() %}
  {% for date, shows in upcomingShows|group("showTime|date_modify('-3 hours')|date('Y-m-d')") %}
    {% for show in shows %}
      {% set movie = craft.entries.relatedTo({ targetElement: show }).one() %}
    {% endfor %}
  {% endfor %}
{% endcache %}

It gives me the following fatal error:

Allowed memory size of 268435456 bytes exhausted (tried to allocate 71002178 bytes)

If I skip the Twig |groupfilter, the cache works again:

{% cache %}
  {% set upcomingShows = craft.entries.section('shows').orderBy('showTime asc').limit(null).all() %}
  {% for shows in upcomingShows %}
    {% for show in shows %}
      {% set movie = craft.entries.relatedTo({ targetElement: show }).one() %}
    {% endfor %}
  {% endfor %}
{% endcache %}

(I first thought the problem was the relatedTo() function, but it turns out it's the |group is what triggers it, without it it works also with the  relatedTo().)

Is this a bug in the cache, or is there a way around it?

UPDATE: After some more testing, it seems like it works either with the |group filter or the relatedTo() function, but not both at the same time.

After looking through this, it would probably be better off as a Github issue than a Stack Exchange post, but answering here for posterity.

In short, this comes down to a change we had to make in Craft 3 to fix some other caching issues where we started to serialize element queries when saving them, so that when retrieving them later to determine if a cache should be busted, we would unserialize and execute the saved element query.

It turns out that serialization requires significantly more memory than not serializing.

In the case of your database, with your custom fields and plugins on both shows and movies sections, it takes about 1.1MB of memory to save each element query inside of your {% cache %} tags.

Given your current template code, with the nested for loops and the number of elements you're accessing in the cache tag, you're looking at about 7,000 element queries that Craft has to track, or 7.7GB of memory - hence the error you're hitting.

It's on our list to completely revamp the template caching engine to help mitigate scenarios like this (as well as other template caching issues people have come across)

In the meantime, you've got a few options.

Revamp your template logic so you're not executing O(n2) database queries.  That could involve pure Twig/templates or a custom plugin where you can grab exactly the data your template needs from PHP/Querybuilder.
Set the cacheElementQueries config setting to false. Craft will no long attempt to record/bust template caches, so you can come up with your own logic for busting them (cron job, plugin, etc.).
Use a plugin like Cache Flag (or a similar one).  It also doesn't attempt to save element queries, but provides a way to bust caches based on keywords.


Getting entries from multiple categories I have two categories, Annual Reviews and Publications.
I want to get all entries from these and paginate them.

The code I am using is fine for one category - both work individually, but as soon as the criteria contains two categories i get zero results no errors. I have tried using 'one()' on the initial categories as I only want category returned.
What am I missing? (using Craft Pro 3.1.31)

{% set publications = craft.categories.group('resources').slug('publications') %}
{% set annualreviews = craft.categories.group('resources').slug('annualreviews') %}

{% set entries = craft.entries({
section: 'resources',
relatedTo: [
'and',
{ targetElement: publications },
{ targetElement: annualreviews }
]
}).limit('10').orderBy('title') %}

{% paginate entries as pageInfo, pageEntries %}

OK resolved now - client slug mis-spelling! thx


Is there a way to override locale data? We are using the Craft's currency filter to display localised prices 344552.32 | currency('GBP') which works great in most instances.

However the currency symbol for UK Sterling in the French locale file is £UK and the translation company we are working with are saying that we just need to output £.

This is defined in the currencySymbols array of craft/app/framework/i18n/data.fr.php.

Is there a way we can affect that at a configuration level or at runtime? If not I guess we would have to create our own filter function that wraps the core functionality and does a string replace on the output.

A further requirement came up to change the Italian output (symbol moved to after the price rather than before) so I opted for a simple Twig extension:

/**
 * Wrapper for Craft's NumberFormatter->formatCurrency
 * Changes formats for French and Italian display
 *
 * @param        $value
 * @param string $currency
 *
 * @return string
 */
public function currencyDisplay ($value, $currency = 'GBP')
{
    $value = craft()->numberFormatter->formatCurrency($value, $currency);
    $localeId = craft()->locale->id;

    if ($localeId == 'fr')
    {
        $value = str_replace('£UK', '£', $value); // remove UK
    }
    else if ($localeId == 'it')
    {
        $value = str_replace('£ ', '', $value) . ' £'; // relocate sterling symbol
    }

    return $value;
}


Can I disable channels in Redactor "Link to entry" feature? I have some channels with entries that I don't want my client to be able to link to using Redactors "Link to entry" feature. Can I disable some channels for a specific field in a specific channel/structure/single?

Here is a super hacky way to do this...but it does do the trick.

Locate the JS file responsible for rendering the Element Selector Modal (craft/app/resources/js/compressed/RichTextInput.js)
Search for: {storageKey:"RichTextFieldType.LinkToEntry". This is where the modal gets called.
Nearby, locate "criteria" and change

{locale:this.elementLocale} to {locale:this.elementLocale, section: ['sectionYouWant','otherSectionYouWant']}
Push this JS file to your server and only the sections you include in the section array above will show entries. The other section titles will show, but no entries will show up to select.

Keep in mind that this adjustment will probably be overriden by any Craft update. I bet building your own redactor plugin would be cleaner.


Plugin: Linking elementSelectField to own plugin models I'd like to have an elementSelectField in my plugin. The goal is to select a Company which is defined by my plugin Model. So when clicked, it'll display a list of companies in the Dialog box and you select or search for them, then close it (Same way as selecting Assets).

Is there an example I can see out there or perhaps some guidance? Do I need my own FieldType?

Cheers,
Ryan

Perhaps I am misunderstanding what you are trying to accomplish, but it seems like you should be able to accomplish what you are looking for with Craft's native EntriesfieldType.
Regardless, I'll write up a few options for creation your own Entry select modal as well as a custom FieldType.

If you check out Craft's BaseElementFieldType which all of Craft's Element Field Types extend, you'll see that it has an attribute called $inputTemplate with the value '_includes/forms/elementSelect'. This is the template that is rendered for element fields on the entry edit pages, and is accessible from the forms macro!
To create an element select modal from within a template, you can call

{{ forms.elementSelect({options}) }}

Don't forget to import the forms via {% import "_includes/forms" as forms %}

If you check out the elementSelect template, you can see all of the fields that are required to create an element select modal. This is really handy and it becomes really easy to create an element select modal. Inspecting an element field type's getInputTemplateVariables() will show you what variables are passed. Use of a debugger here and setting breakpoints to walk through the flow of execution helps a lot as well.

If you check out the EntriesFieldType, you'll see that it relies almost entirely on the logic in the BaseElementFieldType, and just updates the $elementType property, and overrides the getAddButtonLabel() method. So the input template variables defined in the base class are the ones that are used.

Now if you are defining a custom field type, you have this handy tool to create a modal. You can create an input template and make this call, and then implement getInputHtml() to pass the necessary variables and render the template.

Note: If you have never done this before, check out Craft's documentation on how to create your own custom field.

Let's say you want to create a custom field type that is similar to the Entries field type but you want it to have some additional functionality outside of what you get with the EntriesFieldType. No problem! Craft has made it really easy to do this. What you can do is create a field type like you would a normally, (follow the same naming conventions - PluginName_NameFieldType.php within a fieldtypes directory in your plugin), but instead of extending BaseFieldType, you can extend EntriesFieldType and get all of the functionality of the EntriesFieldType for free! Then you can override methods as you see fit in order to create your custom functionality.

For instance, lets say you want your entries select field to only be able to select fields from your Companies channel, you could override the inputSelectionCritieria() method and return

$criteria['id'] = $ids

Where $ids is an array of element ids.

Note: Criteria returned by this method and passed to elementSelect is not a CriteriaModel, but is an array with some pretty specific keys. For some more detailed documentation, check this out, and also check out the BaseElementFieldType implementation.

Extending the EntriesFieldType is really handy because you can rely on Craft for most of the logic. For instance, if you want your Entry select modal to be exactly the same as the default one, then you don't have to define your own input template. You can modify the input variables as you see fit by overriding methods and adding your own custom functionality, and then let the BaseElementFieldType render the modal itself.

This post has been focused on backend stuff for modals and field types, but it is also possible to extend the modal js for a more custom experience. Check out this stack post and Peter Tell's Many to Many plugin for a complete example.

The best tool to figuring out what is going on is inspecting the Craft core as well as other plugins. There isn't great documentation on how to do a lot of this stuff, so if you have more specific questions, please feel free to leave them here! I'd love to expand this answer to have more detailed explanations. Hopefully this can help get you started!


Get the current entry element in the CP (edit entry view)? You can listen to entries.onSaveEntry or entries.onDeleteEntry events and then get or modify
the entry via $event->params['entry'];.

But how would I get the entry element and its properties in the event that a user starts editing
an entry in the CP? I don't (necessarily) need to modify them, simple access is ok.

As far as I can see, there's no event available for this. And if I understand correctly,
only P&T can add such an entries.onBeforeCpEditEntry
event (which would probably go into Entries Controller actionEditEntry function).

If you do entry edits using front-end forms this is easy, but not so from within the CP it seems.
Is there anything I overlooked, or is this simply not possible and maybe worth a feature request?

-

This question is related to this one: "How to reference the current entry element in custom Control Panel templates?"
but it's not quite the same. The solutions / workarounds I posted below have some drawbacks, so I'm still looking for a better solution!

I've been able to modify the entry editing form by creating a plugin that will include some javascript into CP layout. Then with javascript, I use some checks to make sure that I'm in the right place before doing any adjustments.

For example, in the following code, I checked if I'm editing an entry within section 32, then the label of postDate field will be changed, and also the expiryDate field will be hidden.

public function init()
{

    if (craft()->request->isCpRequest())
    {
        $postDateLabel = JsonHelper::encode(Craft::t('Issue Date'));

        $js = <<<EOT

if (document.getElementById('entry-form')) {
    \$(function() {
        if ( \$('input[name="sectionId"]').val() == 32 ) {
            \$('#postDate-field label').html($postDateLabel);
            \$('#expiryDate-field').addClass('hidden');
        }
    });
}

EOT;

        craft()->templates->includeJs($js);

    }

}

Hope that is what you're looking for.


Global User Notification Widget Is it possible to create a Custom Widget that behaves like the 'Deprecation Errors' warning on the Dashboard? When active, the widget would appear on all users', or particular user groups, dashboards at the top and display new information, warnings and anything else that may be of use.

There’s no such thing as a “global” widget right row, however your widget’s getBodyHtml() method can return false, which tells Craft to not show the widget at all. So you could just have the widget assigned to all the user accounts you want, and always return false unless there’s actually something to display.


Assets page "An unknown error occurred" I'm suddenly unable to load my site's assets page. All of my asset sources are local, and loading the main assets page or any source folder or subfolder results in "An unknown error occurred." being displayed. No images are displayed. The assets section of the CP had previously been working fine.

I can still see my assets when uploading an asset within an entry editing screen, though.

The relevant log info (I hope) is below. Seems to be an issue with Imager, which I think was updated recently:

2018-12-10 17:04:05 [-][1][-][error][yii\base\UnknownPropertyException] yii\base\UnknownPropertyException: Getting unknown property: aelvan\imager\models\ConfigModel::imgixEnablePurgeElementAction in /path/to/site/vendor/yiisoft/yii2/base/Component.php:154

Stack trace:

0 /path/to/site/vendor/aelvan/imager/src/Imager.php(154): yii\base\Component->__get('imgixEnablePurg...')

1 [internal function]: aelvan\imager\Imager->aelvan\imager\{closure}(Object(craft\events\RegisterElementActionsEvent))

2 /path/to/site/vendor/yiisoft/yii2/base/Event.php(310): call_user_func(Object(Closure), Object(craft\events\RegisterElementActionsEvent))

3 /path/to/site/vendor/craftcms/cms/src/base/Element.php(320): yii\base\Event::trigger('craft\\elements\\...', 'registerActions', Object(craft\events\RegisterElementActionsEvent))

4 /path/to/site/vendor/craftcms/cms/src/controllers/ElementIndexesController.php(409): craft\base\Element::actions('folder:3')

5 /path/to/site/vendor/craftcms/cms/src/controllers/ElementIndexesController.php(90): craft\controllers\ElementIndexesController->_availableActions()

6 /path/to/site/vendor/yiisoft/yii2/base/Controller.php(155): craft\controllers\ElementIndexesController->beforeAction(Object(yii\base\InlineAction))

7 /path/to/site/vendor/craftcms/cms/src/web/Controller.php(104): yii\base\Controller->runAction('get-elements', Array)

8 /path/to/site/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('get-elements', Array)

9 /path/to/site/vendor/craftcms/cms/src/web/Application.php(282): yii\base\Module->runAction('element-indexes...', Array)

10 /path/to/site/vendor/craftcms/cms/src/web/Application.php(542): craft\web\Application->runAction('element-indexes...', Array)

11 /path/to/site/vendor/craftcms/cms/src/web/Application.php(266): craft\web\Application->_processActionRequest(Object(craft\web\Request))

12 /path/to/site/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))

13 /path/to/site/web/index.php(21): yii\base\Application->run()

14 {main}

As noted in the comments, sounds like you've got an error in your Imager config (prob an incorrect value set for the imgixEnablePurgeElementAction option or one of the lines close by).


Cloning content between locales I am building a bilingual website using Craft CMS. The english entries are completed and I am now in the process of doing translating the content to french. I would like to know if there is a way clone the english entries to the french entries so I can just change fields that are different?

You could build a plugin that loops thru all the entries and creates a new entry programmatically, the only difference in the new entry would be the locale. Look at this: https://craftcms.com/classreference/services/EntriesService#saveEntry-detail, you would include $entry->locale = 'fr_us'; or whatever the shortname/handle of that locale is.

Or export the entries and import them, and setting the locale to french using something like: https://github.com/engram-design/FeedMe


Where is Auto Update? I am attempting to update my craft instance from Craft CMS 2.6.2781 to the latest. (Critical Update).

Unlike before where there was a button that would do everything for me nicely and automatically, this update just downloads a zip file.

Is it possible to auto update this fix? If not are there instructions on what to do for this update? The readme.txt has pretty generic instructions.

Thanks

When the folks at Craft cut a release they can tag an update in one of several ways:

One-click Update
Manual Update

They can also tag their updates with a level of importance:

Normal Update
Critical Update

This helps Craft manage several scenarios that might occur that they need to account for during a release. When you see the "Manual Download" button it probably means that something changed in the code that could fail if an auto-update were performed or that they need to make sure everybody updates to that particular release before updating to any releases beyond that.

In this case, I believe the two things you have identified are from different releases. The Critical Update flag relates to release 2.6.2791 and makes the update badge in the Craft CP red, to indicate its importance. And the Manual Update flag relates to release 2.6.2922 where one of the bugs fixed related to the updater functionality itself, and they needed to trigger a manual download to make sure the files got updated properly.

A longer discussion of Critical Updates is available in this Straight Up Hangout:

Critical Updates and Craft CMS


/admin/actions/users/login 404 When trying to login to my live env on craft, I get the following error in the javascript console.

[Error] Failed to load resource: the server responded with a status of 404 (HTTP/2.0 404) (login, line 0)

This only happens in the live environment, which is weird because the DB and front-end code are synced between local and live. There is also a staging website where the login page works.

What server provisioner are you using? Forge? Something else? If it's Forge, it's most likely this:

https://twitter.com/nystudio107/status/895726366477344772

Also here are some Nginx configs (with Forge examples) that are known to work with Craft CMS 2.x and 3.x: Nginx-Craft


Using multiple 'search' parameter values to filter entries I'm trying to filter entries down based on keywords and also if they have selected a letter that matches the first letter of an entry title on Craft 3.

So far I have this:

{% set keywordParam = craft.request.param('keyword') %}
{% set characterParam = craft.request.param('filterByCharacter') %}

{% set entriesParams = {
   section: 'people',
   level: 1
} %}

{% set searchArray = ['OR'] %}

{% if characterParam|length %}
   {% set searchArray = searchArray|merge(['title:' ~ characterParam ~ '*']) %}
{% endif %}

{% if keywordParam|length %}
   {% set searchArray = searchArray|merge(['title:' ~ keywordParam]) %}
{% endif %}

{% if searchArray|length %}
   {% set entriesParams = entriesParams|merge({ search: searchArray }) %}
{% endif %}

{% set peopleEntries = craft.entries(entriesParams) %}

At the moment this is producing the error:

  Undefined index: query

I've narrowed it down to this line(s):

{% if searchArray|length %}
   {% set entriesParams = entriesParams|merge({ search: searchArray }) %}
{% endif %}

But I can't figure out what is wrong with this, and what could be going wrong!

An example URL is:

  http://localhost:3000/our-people?keyword=sach&filterByCharacter=M

Just looked at the docs (https://docs.craftcms.com/v3/searching.html#supported-syntaxes) and it looks like the accepted syntax for this would be:

title::M* OR title:sach

As a result, modifying your code like so should achieve this:

{% set keywordParam = craft.request.param('keyword') %}
{% set characterParam = craft.request.param('filterByCharacter') %}

{% set entriesParams = {
   section: 'people',
   level: 1
} %}

{% set searchArray = [] %}

{% if characterParam|length %}
   {% set searchArray = searchArray|merge(['title::' ~ characterParam ~ '*']) %}
{% endif %}

{% if keywordParam|length %}
   {% set searchArray = searchArray|merge(['title:' ~ keywordParam]) %}
{% endif %}

{% if searchArray|length %}
   {% set searchParam = searchArray|join(' OR ') %}
   {% set entriesParams = entriesParams|merge({ search: searchParam }) %}
{% endif %}

{% set peopleEntries = craft.entries(entriesParams) %}

Hope that resolves it for you.


How can I set an alias dynamically based on request? I have a multi-domain setup where I run multiple one-pager sites from different domains. For development purposes, I have configured it to work like this:
dev has:
http://localhost/www.domain1.com
http://localhost/www.domain2.com

staging has:
https://stage.ourdomain.com/www.domain1.com
https://stage.ourdomain.com/www.domain2.com

and production has:
https://www.domain1.com
https://www.domain2.com

To achieve this with the same site configuration for all three envs, I have created an alias called @baseUrlPrefix that I use in the site configuration (as part of the base URL), so for each env they are set to:
dev: http://localhost
stage: https://stage.ourdomain.com
prod: https:

And for each site I do @baseUrlPrefix//www.domain1.com (etc)
This has all worked fine and all, but I ran into issues when my devs had to do testing from mobile devices, because their local environment would only respond to localhost URLs. So I made a change for our dev config, to do (inspired by https://github.com/craftcms/cms/issues/2486):
use yii\web\Request;
$request = new Request;
...
'@baseUrlPrefix' => $request->getHostInfo(),

Which has also worked, no problem up until I'm trying to upgrade craft (this works in 3.3.15-18), and onwards from 3.3.19 it seems, it throws because it can't find Yii. It seems like maybe the loading order has changed, or I'm doing something incredibly stupid. I read that @web is supposed to actually take care  of this, which it also does, so using @web/www.domain1.com does work as intended, and I can overwrite that alias on production and set it to https:.
BUT, I also read that it's highly discouraged to use @web as siteUrl (https://craftcms.com/docs/3.x/sites.html#site-url) and I don't fully understand the implications of this, or if my use of an @alias is already suffering fromt he same cache poisoning bug?

As far as I know, using the @web alias is discouraged if its determined automatically (which is the default behavior).
If you override it with a hardcoded value in production I think you should be fine.

Edit
After checking the documentation, the warning is now pretty dissuasive against using the @web alias :

When you actually use it in a site's base URL setting in the control panel, the warning is a little more moderated:

I'm not sure if its because the documentation is more up to date or if it's actually fine to use it when overridden.


Is it possible to move an entire asset source? S3 is seemingly costing us a lot. While it has its benefits, I think we could do with moving images from s3 to the server we are running the site from. How difficult/impossible of a task would that be?

The easiest way I imagine would be to

Create a local folder
Download all the files into the folder (outside of Assets - via Transmit or similar)
Change your source settings from S3 to Local with correct paths

And you're done. Whoa, that was easy :)


Limit category choice in entry to 1 and select child category I have a section of book entries with a categories field called "Genre". A book can fall into multiple genres. A stripped down version of the categories works something like this:

Fiction

Crime, Thrillers & Mystery

Mystery
Thrillers
Action & Adventure

Fantasy

Non-Fiction

etc

On the home page, there is to be a feature panel linking to a particular genre, let's say Thrillers for example.

For the feature panel field, I need to limit this to a single genre, but if I set the limit on the categories field to 1 and select Thrillers, it just selects Fiction.

Of course I could just remove the limit and then use twig to get only entries from the deepest selected category, but I don't want to open up the possibility that they'll select multiple categories.

Is there any way around this? I can't help but feel like the limit option on the categories fieldtype is a bit broken.

* Update for clarity *

Categories group called Genre is structured as above
Books channel has a category field called Genre, allowed to select multiple genres
Homepage has a category field called Related Genre

The intention is to choose a single category for Related Genre to output the books which have that category selected in Genre.

This is all entirely within the admin area, so code is effectively irrelevant at this point.

Perhaps some images will make this clearer:

In the entry, I attempt to select Thrillers:

But after clicking select, it has just selected Fiction:

You could use a structure for Genre rather than categories. This would let you select Thrillers as a single entry.


Craft Error Logging not doing anything? I have a plugin called EducatorEntriesPlugin, and in the controller of the plugin, I have a method called actionExportEducator. In this method, I am logging an error using the code below, but when the action is called, the error is not logged into the log file, why might this be?

Craft::log("There was an error")

I also did EducatorEntriesPlugin::log, that doesn't do anything either.

Turn devMode on https://craftcms.com/support/dev-mode

If you want the errors to log regardless of devMode or not, pass in true as the third parameter, e.g.:

Craft::log("There was an error", LogLevel::Info, true);

https://straightupcraft.com/articles/how-do-i-log-errors-warnings-and-info-in-my-plugins-to-a-craft-log-file


Invalid asset source path After setting an asset resource I am receiving the "your source ' ' appears to be invalid." on the update indexes, and

Error uploading the file: The file system path '{basePath}/assets/images/' set for this source does not exist.` on the assets upload page.

My file structure is:

/var/www/
        craft/
        html/
            index.php
            assets/
                images/

I've tried all variations I can think of, ie:

{basePath}assets/images/
/assets/images/
/var/www/html/assets/images

I've ensured that the basepath is set correctly in general.php and i've ensured that ownership of the folders is set correctly.

Craft is installed on a digital ocean lamp droplet running 14.04. general.php looks like:

return array(

  // Use IP address of your droplet below
  'ip.address.of.droplet' => array(
    'siteUrl' => 'http://ip.address.of.droplet/',
    'environmentVariables' => array(
      'basePath' => '/var/www/html/',
      'baseUrl'  => 'http://ip.address.of.droplet/',
    )
  )
);

the general.php file was setup for a multi environment but was missing

'*' => array(
),

as per the notice here:
https://craftcms.com/docs/multi-environment-configs

{basePath}assets/images/ was the correct file system path once the array had been added.


Contact Form Plugin P&T redirect issue with locales I'm using the standard P&T contact form plugin. I have a website with 3 locales in a folder structure where every locale has its own folder:

Public

-- EN

-- FR

-- NL

The contact form has the following code:

<form method="post" action="" accept-charset="UTF-8" data-abide>
        {{ getCsrfInput() }}
        <input type="hidden" name="action" value="contactForm/sendMessage">
        <input type="hidden" name="redirect" value="thanks?from={fromName}">

        // Rest of code: fields, etc.

</form>

The redirect template is called thanks and it works as it should except that the thanks page is always redirecting to the EN locale.
When I manualy navigate my browser to go to the thank you page in another locale it works too. But after submitting the form, it just does not redirect to the current locale.

Update

Removing ?from{fromName} does not resolve the issue

siteUrl is set in the config file:

'*' => array(
'siteUrl' => array(
                'nl' => 'http://cmgg.buthost.com/nl/',
                'fr' => 'http://cmgg.buthost.com/fr/',
                'en' => 'http://cmgg.buthost.com/en/'
            )
)

Sidenote: the whole website is almost final and everything with the locales is working as it should except the contact form redirect.

Remove 'usePathInfo' => true, from your /craft/config/general.php

This fixed a similar error on my site.


limiting profiles to user groups Can profile fields be limited to user groups in backend? It seems that if you want required front end fields for users registering then admins in backend have to have these as compulsory when creating admin accounts.

Custom fields are attached to Users, not User Groups.

Seems like you'd be able to do what you want on the template side, though.  i.e. something like "if user is in group A, show field B, otherwise hide it".

Or perhaps something like the Reasons plugin is the way to go, which adds conditionals to fields/field layouts.


Saving Form Data to Plugin Record via Javascript I am new to Craft plugin development, so I apologize ahead of time for my lack of understanding.

I'm working on a plugin that allows the user to "upload" Excel/csv files via the control panel that then get sent to an API via Javascript. I then want to save the returned data to my plugin's record.

At the moment, I have a JSON object with all the necessary data and a record set up as such:

class GeocodeFile_LocationsRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'geocodefile_locations';
    }

    protected function defineAttributes()
    {
        return array(
            'id'       => array(AttributeType::Number, 'required' => true),
            'type'    => array(AttributeType::Bool, 'default' => false),
            'name'       => array(AttributeType::Name, 'default' => null),
            'address'        => array(AttributeType::Name, 'default' => null),
            'city'         => array(AttributeType::Name, 'default' => null),
            'state'   => array(AttributeType::String, 'default' => null),
            'zip'       => array(AttributeType::Number, 'default' => false),
            'url'     => array(AttributeType::Name, 'default' => true),
            'lat'   => array(AttributeType::String, 'default' => null),
            'lng' => array(AttributeType::String, 'default' => null)
        );
    }
}

Is it possible to save this JSON data to the record directly, or is there a way to create a model in Javascript that could then be saved to the table? Perhaps I can send the data to a service that would handle it instead?

Clearly, I don't know how this is supposed to be handled. Any help with the matter would be greatly appreciated.

Sorry again for my naiveté on this subject!

Assuming you've got a controller endpoint in your plugin that has an action, the pseudocode for a simple solution could be something like:

Grab the JSON data from post.
json_decode it.
Loop through the JSON data.
Instantiate a new record and assign the properties to it inside the loop.

i.e.

$record = new GeocodeFile_LocationsRecord();
$record->id = $jsonData['id];
$record->type = $jsonData['type'];
...
$record->save();


Remove outdent & indent from Redactor2 "lists" button Redactor 2 introduced a single "lists" option for ordered and unordered lists but unfortunately this adds the outdent and indent options in as well.
Any way of removing outdent/indent again?

I've managed to do this using a plugin which allows you to write CSS affecting the Craft CP.

Once: https://github.com/lindseydiloreto/craft-cpcss is installed, click Settings > Control Panel CSS and add this to the "Additional CSS" field...

.redactor-dropdown-outdent, .redactor-dropdown-indent {
    display: none !important;
}

Here's a screen shot:

This is what I end up with in Redactor...


DeleteStaleTemplateCaches stalled tasks I have a dev site that I'm working on with around 1000 entries - mainly text content. I keep seeing "DeleteStaleTemplateCaches" stalled tasks come up - many times stacking 6-8 sets of similar stuck tasks.

I've read the article about stalled tasks but it seems to say they are the result of using a cache tag. I have no caching of any kind - no cache tags and no caching on the server (that I'm aware of). I'm sometimes able to clear the tasks using Task Manager (after many tries) but they always return.

Is there anything else I should check? Thanks in advance.

Thanks to Brad Bell who solved this for me. There was data in my craft_templatecache* tables. When I cleared them out the issue went away.


Allowing non staff users to access CP to post and edit posts? I ask this in the context of a sharing content site, let's say a forum.
This doesn't seem to be a recommended practice, and I wish to know if there is a particular reason:

I plan to let non staff, but registered site visitors to access the CP, to post content in specific allowed categories of my site. Access to the cp is a HUGE time saver: user can post entries, with all the CSS/JS work done, which means that they can benefit from autocomplete tags and all the goodies from the CP. They can see a list of all their entries, edit them etc etc....

Why should I bother doing custom forms if all can be done with CP access ? Any reason ?
I don't believe there is a security concern since the CMS allows very precise control of permissions and what user in a group can do in the CP.

You're right: there's no technical reason why you couldn't do what you're asking. When users start to add content for a site, they're now content contributors. And it can certainly make sense—depending on the site. When I think of "content sharing site", Craft could be used like a Wiki, for example.

The short answer? If you find yourself reinventing what Craft can already do, just give someone access to the CP.

A longer answer and reasons not to do it? Control. User experience. And trust/security all come to mind.

I just created a site for a client where individual teachers are able to post their class schedules and such and people are able to buy those classes (via Stripe). The client loved the CP. All the teachers were impressed about how easy it was to add their schedules. The CP was definitely a plus in this area.

However, the client properly scrutinizes everyone who wants to use the site (and their credentials) and there's a training period where we show people how to use the site, where to login, etc.  Your use case might be a little more streamlined if you have computer savvy users, etc.

While Craft is straightforward, if not downright intuitive, the CP might be overkill for someone just casually posting content once in awhile.   There still is a lot going on. And it really depends on what content you're managing.

From a traditional web context, the CP is a still little "disconnected" from the actual website. You still have to find the page you want to edit, or create a new one, preview it, and publish.  (That's why, IMO, things like "in-place" editors on are still popular with other CMS.... though I hate those things!)

Having the CP be disconnected is partially due to security. If you trust and properly vett your authors, or there's not much on the line,  I see no reason why you couldn't open up the backend.

As always, take good backups in case you have to roll back to something. That's true of any server, though. No software is perfect; if there's a vulnerability found for, say privilege escalation where a user could somehow become an administrator, the more users that have access to the CP, the more chances someone has to breach the site. Craft is relatively niche so you won't have the brute-force drive-bys that something like WordPress has. But as a site administrator, you can never be too vigilant.


How to bootstrap / connect to another Craft instance from inside a plugin? I'm currently writing a plugin which should connect to another Craft Instance (same server, same app folder) and run some code (start a task or execute a service function).

I looked at the bootstrap.php which looks great, but I can't include it inside the plugin, or can I somehow?

So how can I bootstrap / connect to another Craft instance from inside my plugin?

I don't see why you wouldn't be able to load it up from your plugin.

public function init()
{
    $otherCraftApp = require '/path/to/other/bootstrap.php';
    $otherCraftHttpRequestService = $otherCraftApp->request;
}


More than one field for a fieldtype plugin I can have many 'settings' fields for a fieldType and they're saved as a serialized string in the database.

Is it possible to have more than one field for the fieldType itself? It looks like there's only one column in craft_content. So I'm not sure you can.

Is the preferred way to create your own table and store multiple field data there? And would that be using prepValueFromPost()?

Nope – a single field can only have a single column in the craft_content table.

If you need your FieldType to save complex data, you have two options:

The easiest approach – by far – is to simply have Craft serialize the data before it's saved to the craft_content table. Simply return AttributeType::Mixed from your FieldType class' defineContentAttribute() method:

public function defineContentAttribute()
{
    return AttributeType::Mixed;
}

With the above, your FieldType's value will be an array. This means that when you save the data, you'll need to use the array syntax for the input fields' name attributes; e.g. if you had two "sub fields" called foo and bar, your input fields should look like this:

<input type="text" name="myFieldHandle[foo]" />
<input type="text" name="myFieldHandle[bar]" />

To output the sub fields in a template, you'd do something like this:

{{ entry.myFieldHandle.foo }}
{{ entry.myFieldHandle.bar }}

The other approach for saving complex values is to have your FieldType use a custom database table. Explaining this in any great detail is impossible without knowing a lot more about your particular use case and a good overview is already given in Ben Parizek's great answer to a similar question – but here's the gist of it:

First, you'll need to create a Record class, containing your custom FieldType's columns (sub fields) and their attribute types. Whenever your plugin is installed, Craft will create custom database tables based on your plugin's Records (which means that if your plugin is already installed, you'll need to uninstall and install it again to actually have Craft create the tables – if this is a publicly available plugin you should probably write a Migration to ensure the table is created whereever the plugin is already installed).

Note that your custom table should probably have foreign keys for the element and field ID's, to create the necessary relations between the field data, the field and the actual element (e.g. entry).

To actually save the data to your custom table, you'll need to add a onAfterElementSave hook to your FieldType class. This hook is run whenever an element with a field using your custom FieldType is saved. The field and element ID's (and the actual data to save), is available via the FieldType's model and element properties, respectively:

public function onAfterElementSave()
{

    $element = $this->element; // The element being saved
    $elementId = $this->element->id; // The ID for the element being saved
    $fieldModel = $this->model; // The field's model
    $fieldHandle = $field->model->handle; // The field's handle
    $data = $this->element->getContent()->getAttribute($fieldHandle); // The data you want to save

    ...

    // Some custom logic to save the data

}

To save the data via the onAfterElementSave hook, you'll most likely need to use the DbCommand class to build a custom query.

In order to populuate your field (i.e. pull the data) for use in templates and the getInputHtml() method, you'll also need to write a custom query. A good place to do this is in your FieldType's prepValue() method, i.e. something like this:

public function prepValue($value) {
    $value = null;
    $fieldId = $this->model->id;
    if ($this->element->id) {
        // Then element has been saved before and there might be existing data – try to pull it
        $value = craft()->db->createCommand()
            ->select('foo, bar')
            ->from('yourcustomdatabasetable')
            ->where([
                'fieldId' => $this->model->id,
                'elementId' => $this->element->id,
            ])
            ->queryAll();
    }
    return $value;
}

Finally, to save some overhead its a good idea to return false from your FieldType class' defineContentAttribute method (this basically tells Craft to not save anything to the craft_content table):

public function defineContentAttribute()
{
    return false;
}

Note that there are other aspects to consider as well, such as offloading the business logic in saving and retrieving the data to a service layer (a good idea), and using Models to pass data between records, the service layer and your templates (a very good idea), but it's a bit out of scope for an SE answer like this. In any case, I hope the above will get you up and running. In any case, if you simply need to two text fields, I'd go with the first approach – it's way easier.


Template query: Advanced section query to get unique items from custom entry type field I'm trying to build a filter for a blog section in Craft 3.

I'm fairly new to Craft and my client now wants a dropdown filter where they can filter out authors. Thus, I want to write a query that fetches a unique list of authors from my blog channel structure.

The structure is:
section: blogEntries (Channel) with an entry type blogEntry.

Each blogEntry has a content (matrix) and and writer entry field which is connected to one or more employee entries.

So; blogEntries > blogEntry > writer(s) > employee

What I want is something like

craft.entries.section('blogEntries').column('writer').distinct().all()

This is what I have:

{# Way too slow #}
{% for item in craft.entries.section('blogEntries').all() %}
    {% if item.writer|length %}
        {% set person = item.writer.one() %}
        {% set name =  person.firstname ~ (person.surname|length ? ' ' ~ person.surname : '') %}
        {% if name not in writers %}
            {% set writers = writers|merge([name]) %}
        {% endif %}
    {% endif %}
{% endfor %}

Terrible solution, terrible performance.

How can I improve this code? Any ideas would be appreciated.

Try this:

// Return all ids for your posts' section
{% set postIds = craft.entries.section('blogEntries').ids() %}

// Look for and return employees which have a relation with any of the posts returned in 'postIds'
{% set writers = craft.entries.section('employee').relatedTo({ sourceElement: entryIds }).all() %}

// Loop and display
{% for writer in writers %}
    {{ writer.title }} <br>
{% endfor %}

This will only return a list of writers who have written a post, not a list of all writers but I guess that's what you want..?


Reverse Assets fixedOrder Is there a way to reverse the Assets field keeping the fixed order? I've tried a couple of things:

myAssetsField.order('fixedOrder desc') //breaks
myAssetsField.order('id desc') // wrong, since they're not sorted by id
myAssetsField.reverse() // does nothing

Any ideas?

You could try twig's reverse filter.

{% for asset in entry.assetsField|reverse %}
    <img src="{{ asset.url }}" alt="{{ asset.title }}"/>
{% endfor %}


Using prevSiblingOf and nextSiblingOf Super simple problem. I'm on a news item page (not a paginated overview page) and want to show a link to the next news item in the Channel; So I can't understand why this is outputting nothing at all:

{{ craft.entries.section('news').nextSiblingOf(entry).title }}

I can't seem to get either of the SiblingOf properties to do anything. What simple mistake am I making?

(I have also tried the following to no avail...)

{{ craft.entries.section('news').nextSiblingOf(entry).first().title }}

Those parameters are only meant to be used on entries within Structure sections. (Just updated the docs to make that more clear.)

If you want to get the next/previous entries within a Channel section, you can use EntryModel::getNext() and getPrev().

There’s a bit of a trick with those functions though, because “next” and “previous” are ambiguous. (Only consider the entries in a specific channel? Or just the ones with a specific custom field value? etc.) So you need to pass element criteria params into those functions to establish what the full order of entries is.

In your case, sounds like you want the next/previous entries within your news section. I’ll assume you want those entries to be listed by Post Date in descending order (so the latest stories are shown first), and the “Next” entry in this case would be whatever story came before the current one; and the “Previous” story would be whatever came later. (If my assumption is wrong, just change desc to asc in the order param.)

{% set newsCriteria = craft.entries.section('news').order('postDate desc') %}
{% set prevEntry = entry.getPrev(newsCriteria) %}
{% set nextEntry = entry.getNext(newsCriteria) %}

{% if prevEntry %}
  {{ prevEntry.title }}
{% endif %}

{% if nextEntry %}
  {{ nextEntry.title }}
{% endif %}