We've got a plugin. It doesn't have the ability to add custom field types. We're pre-defining all of the fields. One of the fields needs to be an Asset field. A user should be able to upload an Asset using the native Craft Asset UI, store that Asset locally or on Amazon or any supported cloud platforms.

How do we do this?

Where do we create the relationship between the uploaded Asset and the database record we want the Asset related to?

If we were allowing custom fields we could have our custom table relate to a Field Layout ID, and have any number of fields relate to that Field Layout. But in our case, we're defining all of the columns we need in our template, in our own record, and don't really need to create any fields within Craft's fields table as we're setting them all up beforehand.

Do we need a Field Layout in this situation? Do we need to create a Field within Craft under our own context (like Matrix blocks) to be able to control the settings such as where to upload the assets to, and whether to restrict the upload to a single folder, or single image?

I feel I have lots of pieces of the puzzle, but can't yet see how they fit together.

2 Answers 2


I did this recently with my Ad Wizard plugin... There are several different parts which need to be brought together to make it work:

In your Record file, add this to defineRelations:

'asset' => array(static::BELONGS_TO, 'AssetFileRecord')

In your Model file, add this to defineAttributes:

'assetId' => AttributeType::Number

Whichever template is responsible for editing that data will need something like this:

<div class="field" id="assetId-field">
    <div class="heading">
        <label for="assetId">Image</label>
    <div class="input">
        {% if assetsSourceExists %}
            {{ forms.elementSelect({
                elements: elements,
                id: 'assetId',
                name: 'assetId',
                elementType: elementType,
                criteria: {"kind":[],"localeEnabled":null,"locale":"en_us"},
                sourceElementId: assetId,
                jsClass: 'Craft.AssetSelectInput',
                addButtonLabel: 'Select Image',
                limit: 1,
            }) }}
        {% else %}
            <p class="error">No assets sources currently exist. <a href="{{ newAssetsSourceUrl }}">Create one now...</a></p>
        {% endif %}

You'll notice in the Twig code above, I set references to the following variables:

  • assetId
  • elements
  • elementType
  • assetsSourceExists
  • newAssetsSourceUrl

These variable were set in the actionEditMyElement method of the Controller prior to loading the template:

// Set assetId
$variables['assetId'] = $myModel->assetId;

// Set elements
if ($variables['assetId']) {
    $asset = craft()->elements->getElementById($variables['assetId']);
    $variables['elements'] = array($asset);
} else {
    $variables['elements'] = array();

// Set elementType
$variables['elementType'] = craft()->elements->getElementType(ElementType::Asset);

// Set assetsSourceExists
$sources = craft()->assets->findFolders();
$variables['assetsSourceExists'] = count($sources);

// Set newAssetsSourceUrl
$variables['newAssetsSourceUrl'] = UrlHelper::getUrl('settings/assets/sources/new');

// Render the template!
$this->renderTemplate('myplugin/_edit', $variables);

Your Controller will probably have something like this in your actionSaveMyElement method:

$myModel->assetId = craft()->request->getPost('assetId', $myModel->assetId);

Your Service file will probably have something like this to save your record:

$assetId = (!empty($myModel->assetId) ? $myModel->assetId[0] : null);
$myRecord->assetId = $assetId;

Hope that helps, let me know if I missed anything!

  • Thanks Lindsey. This is great. I'll need to contemplate this for a moment! Are you allowing the user to select any asset folder? Do you have any Asset field settings in your own plugin, or just let the user interact with all asset folders and such via the Asset Element Selector Modal? Jun 25, 2014 at 1:00
  • The way I've got it setup, the user can select from any existing assets folder. I didn't need to lock it to a single folder for this plugin. Other than borrowing the Twig code which triggers the Asset Element Selector Modal, I tried to let Craft deal with the assets as natively as possible.
    – Lindsey D
    Jun 25, 2014 at 5:22
  • 1
    @VictorIn It's a little bit of personal preference... I generally try to put as much logic as reasonably possible into the PHP, to save Twig the hassle of parsing it later.
    – Lindsey D
    Jun 25, 2014 at 16:15
  • 1
    Thanks for the detailed description Lindsey. This came together nicely and I've now got multiple image fields working as needed. Jun 30, 2014 at 11:49
  • 1
    @LindseyD wins the internet again. Jul 27, 2014 at 23:54

Looks like you have to do a bit of fudging if your running the $model->validate() function in your controller. assetId has to be set correctly before it jumps over to Service.

I still run the following in my Controller

$reorder = craft()->request->getPost('assetId', $model->assetId);
$model->assetId = (!empty($reorder) ? $reorder[0] : null);

Then I run this in my Service

$record->assetId = $model->assetId;

Tell me if I've missed something here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.