6

I'd like to create a field type for my plugin called "location" with two text boxes: one that holds a coordinate for latitude, and one for longitude. I haven't figured out yet how to map those two values from HTML input elements in a template to a JSON array in the database. Is there an elegant way to achieve this with the Craft API?

6

You can do this simply by setting your field type’s defineContentAttribute() method to return AttributeType::Mixed, which will tell Craft to JSON-encode its value before storing it in the database.

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

Then, whatever you submit in the field’s post data will be saved as-is.

For example, if your getInputHtml() method had this:

public function getInputHtml($name, $value)
{
    $lat = (isset($value['lat']) ? $value['lat'] : '');
    $lng = (isset($value['lng']) ? $value['lng'] : '');

    return '<input type="text" name="'.$name.'[lat]" value="'.$lat.'">' .
           '<input type="text" name="'.$name.'[lng]" value="'.$lng.'">';
}

Then it would be expected that the resulting post data for your field would look something like:

array(
    'lat' => '12345',
    'lng' => '67890'
)

And with AttributeType::Mixed, that’s exactly how it would get stored. Behind the scenes it will get JSON-encoded when getting saved to the DB, and JSON-decoded when getting fetched from the DB, but your field type will never need to know about that.

  • Thanks! I wasn't aware of the Mixed attribute type when I posted this. Looks like the JS and the hidden field was extraneous. – Angela T Oct 9 '14 at 19:26
3

Something like this (Let's call the fieldtype Geo):

Your field html should output those two fields, and update a hidden input with the correct namespaced fieldname. Get it like this:

$id = craft()->templates->formatInputId($name);
$namespaceInputId = craft()->templates->namespaceInputId($id);

See docs here about binding JS to fields.

Your fieldtypes content should be defined as Mixed. Craft will encode/decode it to JSON when saving the model automatically.

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

In your fieldtypes prepValueFromPost method, you do all the conversion magic for saving. Let's say your JS save the lat/lng values as a pipe separated value in the hidden input field.

public function prepValueFromPost($value)
{
    if ( empty($value) )
    {
        return new Geo_GeoModel();
    }

    $coordinates = explode('|', $value);
    $geo = new Geo_GeoModel($coordinates);
    return $geo;
}

You also need to hook up the model with the defined attributes:

class Geo_GeoModel extends BaseModel
{
    protected function defineAttributes()
    {
        return array(
            'lat' => AttributeType::String,
            'lng' => AttributeType::String,
        );
    }

    public function render()
    {
        $render = craft()->templates->render('geo/templates/render', $this);
        return $render;
    }

    public function __toString()
    {
        return $this->render();
    }
}

I was actually going to make a plugin that does the same. Please share if you do :)

  • Hey Fred, you may have already seen this, but I built a plugin which contains those fields (and more)... doublesecretagency.com/plugins/smart-map/docs/… – Lindsey D Sep 17 '14 at 5:32
  • Excellent! JS and a hidden input were exactly what I was thinking, but I wasn't sure if that was the "right" way to do it. – Angela T Sep 17 '14 at 13:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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