Search code examples
symfony1apostrophe

Apostrophe CMS: Engine Creation


I'm attempting to create a Product Engine for Apostrophe. I'm having trouble extending the Page Settings form, at the moment I want to add a simple textarea to add a synopsis to the page - eventually I want to add Product settings but I need to get the basics working first.

I've created a form and a settings partial, it's displaying fine and saving the data (with the help of a little hack - might not be correct). The trouble I'm having is when you edit a page the data is not being pulled back in to the form. To be honest I probably doing something fundamentally wrong but I lack experience in Symfony.

My table schema

ccProduct:
  tableName: cc_product
  actAs:
    Timestampable: ~
  columns:
    page_id:
      type: integer
      notnull: true
    synopsis:
      type: text
  relations:
    Page:
      class: aPage
      local: page_id
      foreign: id
      type: one
      onDelete: CASCADE

My form ccProductEngineForm.class.php

class ccProductEngineForm extends ccProductForm
{
  public function __construct($object = null, $options = array(), $CSRFSecret = null)
  {
    // when editing the page the values are not show, this is an attempt to get it to work - it still doesn't :(
    $page_id = sfContext::getInstance()->getRequest()->getParameter('id');
    sfContext::getInstance()->getRequest()->setParameter('page_id', $page_id);

    $ccProduct = Doctrine::getTable('ccProduct')->findOneByPageId($page_id);
    if ($ccProduct) {
      sfContext::getInstance()->getRequest()->setParameter('id', $ccProduct->getId());
    }


    // aPageForm object is passed in
    parent::__construct(null, $options, $CSRFSecret); // construct normally
    //$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?

  }

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

    $this->useFields(array('synopsis'));

    $this->widgetSchema->setNameFormat('enginesettings[%s]');
    $this->widgetSchema->setFormFormatterName('aPageSettings');
  }

  protected function doSave($con = null)
  {
    // page_id is missing! possible bug? BaseaActions.class.php ~ 520
    $this->values['page_id'] = sfContext::getInstance()->getRequest()->getParameter('enginesettings[pageid]');

    parent::doSave($con);
  }

}

Thanks in advance for any help

EDIT:

Thanks for your answer Tom, I'll try to add a little more detail.

I was aware that a page object is passed into the Engine, but I wasn't exactly sure what do to with it - see my confused line of code:

//$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?

To clarify my 'product' is a page that uses the ccProduct engine. I now want to add extra information to that page. Does that make sense? In your words..

Are you trying to actually create a unique product that has its sole "home" on a product engine page? That's what subclassing ccProductForm would do

Yes :)

EDIT 2:

Following Tom's first suggestion (Apostrophe CMS: Engine Creation) I was able to extend the aPage table with my extra fields and the Engine is now saving these.

However, the standard aPageTable::getPagesInfo function isn't returning the fields I saved. I assume I'll have to select these separately?

EDIT 3:

aPageTable::retrieveBySlug() will do the job :)

REVISITED

I decided to revisit this and try Tom's second approach..

The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one

I managed to get this working, my ccProductEngine form constructor now looks like this..

class ccProductEngineForm extends ccProductForm
{
  public function __construct($aPage = null, $options = array(), $CSRFSecret = null)
  {
    $page_id = $aPage->getId();

    if ($page_id) {
      $product = Doctrine_Core::getTable('ccProduct')->findOneByPage_id($page_id);
      if ($product) {
        $ccProduct = $product;
      } else {
        $ccProduct = new ccProduct();
      }
    }

    parent::__construct($ccProduct, $options, $CSRFSecret);
  }

I hope this helps someone :)


Solution

  • There are two approaches you could follow here.

    One is to just extend the schema of aPage in your project level config/doctrine/schema.yml file:

    aPage:
      columns:
        synopsis:
          type: text
    

    Now every aPage object has a synopsis column, which will be null by default, and your engine settings form can just manipulate that one column. Your engine form subclasses aPageForm. You don't need a constructor at all (the default one will suit you), and your configure() method is just:

    $this->useFields(array('synopsis'));

    Boom, you have a textarea for the synopsis that appears when the page type is set to this engine. You don't need a ccProduct table at all.

    The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one. Your engine form class then does not subclass aPageForm, and your constructor has to use the page passed to it to fetch the related ccProduct object (using a Doctrine relation) or create a new one if there is none yet. This is not difficult, but so far it looks like you can keep it even simpler by just adding a column to aPage.