Jump to content

Assigning Fields to Repeaters with the API


thetuningspoon
 Share

Recommended Posts

Hi!

I have been struggling with this for the last few hours and could not find a solution for it anywhere on the forums. I want to create a repeater field using the API and then add fields to it programatically. I can create the field and assign it to a template alright, but I can't assign any fields to the repeater itself.

I've tried using $field->repeaterFields when creating the field but cannot figure out what it's supposed to accept. I tried passing it an array of IDs as well as a fieldgroup, but I couldn't get either to work.

Any ideas?

Link to comment
Share on other sites

Sorry, I don't have much time, and maybe someone else will provide a simpler answer, but I went through the process of figuring this out for a module. It was more complicated because I was creating the repeater fields from JSON, but if you search through this code:

https://github.com/adrianbj/ProcessPageTreeMigrator/blob/master/ProcessPageTreeMigrator.module for "repeater" you should find what you need in there.

Sorry I can't be of more help right now.

Link to comment
Share on other sites

Thanks adrian, but I'm having trouble following along with what's happening in your code.

I discovered that the system is automatically creating a system template/fieldgroup for the repeater with the repeater_xxx naming convention, but it's not created until after I've gone into the admin and gone in to edit the repeater field. So I tried grabbing the fieldgroup after creating the field in the code and adding to it right away, but it seems that the fieldgroup doesn't actually exist yet?

So yeah, I think I'm going to need a simpler answer :)

Link to comment
Share on other sites

Adrian, I just found your post here: http://processwire.com/talk/topic/4736-add-inputfieldrepeater-to-module-configuration-page/

I tried replicating your process for setting up the fieldgroup, field, and template, but PW keeps creating a filedgroup & template with a different name (adding a 1 to the end of it) instead of using the one I created.

EDIT:

Here is my code:

$repeaterName = "sc_promos";
$f = new Field();
			$f->type = $this->modules->get("FieldtypeRepeater");
			$f->name = $repeaterName;
		
			$repeaterFg = new Fieldgroup();
			$repeaterFg->name = "repeater_$repeaterName";
			
			//Add fields to fieldgroup - add others as necessary
			$repeaterFg->append($this->fields->get("sc_promo_active"));
			
			$repeaterFg->save();
			
			$repeaterT = new Template();
			$repeaterT->name = "repeater_$repeaterName";
			$repeaterT->flags = 8;
			$repeaterT->noChildren = 1;
			$repeaterT->noParents = 1;
			$repeaterT->noGlobal = 1;
			$repeaterT->slashUrls = 1;
			$repeaterT->fieldgroup = $repeaterFg;
			
			$repeaterT->save();
			
			//Now, add the fields directly to the repeater
			$f->repeaterFields = $this->fields->get("sc_promo_active");
			
			$f->save();
			
			//Add the sc_promos repeater field to the sc-promo-codes fieldgroup
			$fg->add($f);
			$fg->save();
Link to comment
Share on other sites

Good find - I had forgotten about that post :)

I just used the code from that other post of mine on a fresh PW install (latest dev, although I think it should work on 2.3 stable as well) and it works perfectly.

I know that when I was playing around with writing that code I often messed up the PW database to the point where I was getting errors and found the best way was to do a fresh install again.

BUT, I just noticed that you are missing these key lines in your version:

$repeater_page = "for-field-{$f->id}";
$f->parent_id = $this->pages->get("name=$repeater_page")->id;
$f->template_id = $repeater_template->id;
$f->repeaterReadyItems = 3;

This is critical to make things work.

Let me know how you go.

  • Like 3
Link to comment
Share on other sites

YESYESYESYESYES!!!!  That was it! I thought that that particular bit of code wasn't necessary because I only wanted to create the repeater and not any associated pages. I'm not sure why it works but it does :)

Yes, I've had to go in and manually hack the database due to errors along the way, but all is working well, so I don't think I've messed up anything too bad :)

I do have to say... this is way complicated for ProcessWire since most things in PW tend to be surprisingly easy. But I don't think Ryan foresaw repeaters being created this way. I'm sure this is something that could be simplified in the future.

  • Like 1
Link to comment
Share on other sites

Well, I'm no pro at this and you could probably improve it, but here's my attempt which serves my current needs pretty well:

	/**
	 * Creates a repeater field with associated fieldgroup, template, and page
	 *
	 * @param string $repeaterName The name of your repeater field
	 * @param string $repeaterFields List of field names to add to the repeater, separated by spaces
	 * @param string $repeaterLabel The label for your repeater
	 * @param string $repeaterTags Tags for the repeater field
	 * @return Returns the new Repeater field
	 *
	 */
	public function createRepeater($repeaterName,$repeaterFields,$repeaterLabel,$repeaterTags)
	{
		$fieldsArray = explode(' ',$repeaterFields);
		
		$f = new Field();
		$f->type = $this->modules->get("FieldtypeRepeater");
		$f->name = $repeaterName;
		$f->label = $repeaterLabel;
		$f->tags = $repeaterTags;
		$f->repeaterReadyItems = 3;
		
		//Create fieldgroup
		$repeaterFg = new Fieldgroup();
		$repeaterFg->name = "repeater_$repeaterName";
		
		//Add fields to fieldgroup
		foreach($fieldsArray as $field) {
			$repeaterFg->append($this->fields->get($field));
		}
		
		$repeaterFg->save();
		
		//Create template
		$repeaterT = new Template();
		$repeaterT->name = "repeater_$repeaterName";
		$repeaterT->flags = 8;
		$repeaterT->noChildren = 1;
		$repeaterT->noParents = 1;
		$repeaterT->noGlobal = 1;
		$repeaterT->slashUrls = 1;
		$repeaterT->fieldgroup = $repeaterFg;
		
		$repeaterT->save();
		
		//Setup page for the repeater - Very important
		$repeaterPage = "for-field-{$f->id}";
		$f->parent_id = $this->pages->get("name=$repeaterPage")->id;
		$f->template_id = $repeaterT->id;
		$f->repeaterReadyItems = 3;
		
		//Now, add the fields directly to the repeater field
		foreach($fieldsArray as $field) {
			$f->repeaterFields = $this->fields->get($field);
		}
		
		$f->save();
		
		return $f;
	}

And here's an example of calling it:

$f = $this->createRepeater("sc_promos","sc_promo_active sc_promo_code sc_promo_discount","Promotional Offer","shoppingCart");

You can then use $f to add your new repeater field to a fieldgroup/template.

  • Like 12
Link to comment
Share on other sites

Nice work - that's pretty much exactly what I was thinking.

I am not sure about setting the flags to system and permanent though as the defaults for a function. I think there should at least be a comment on that line that users should adjust to their needs as I don't think this is a typical use case.

Glad to see that code of mine is getting some use - I have to say it was quite an effort to figure out and as you said, very un-PW in its complexity. I would love to hear from Ryan as to whether there is an easier way to achieve this.

Also, for the sake of putting all the information about creating and working with repeaters from the API in one thread, I think it is worth mentioning the special getNew() method for creating repeater items. It is used in the Page Tree Migrator module and described in detail on this page: http://processwire.com/api/fieldtypes/repeaters/

  • Like 3
Link to comment
Share on other sites

Nice work - that's pretty much exactly what I was thinking.

Thanks!

I am not sure about setting the flags to system and permanent though as the defaults for a function. I think there should at least be a comment on that line that users should adjust to their needs as I don't think this is a typical use case.

Good point. I removed that line from the code altogether.

Link to comment
Share on other sites

  • 3 months later...

Thanks everfreecreative for that convenient function, I was fiddling around with that myself atm.

Is is save to delete the template, fieldgroup and field to delete the whole thing or will there be any unused relational pairings left in the database?

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 months later...
Is is save to delete the template, fieldgroup and field to delete the whole thing or will there be any unused relational pairings left in the database?

I know this is old and I am not sure if this is still the case, but when I last looked into this I actually had to do the following to fully clean up a repeater field:

$current_field_id = $this->fields->get("field_name")->id;

$forfieldid = "for-field-$current_field_id";
$sql = "DELETE FROM pages WHERE name=:forfieldid";
$query = $this->wire('database')->prepare($sql);
$query->bindValue(':forfieldid', $forfieldid);
$query->execute();

Here's a related Github issue (https://github.com/ryancramerdesign/ProcessWire/issues/368) and forum post (https://processwire.com/talk/topic/4440-deletepage-true-doesnt-delete-repeated-fields-on-230/). Note that Post #10 by yours truly describes the issue which the above fix deals with. I think this is different to the other things described.

I know this has become a little off-topic now, but wanted to respond to owzim's question with some details and potential issues.

  • Like 1
Link to comment
Share on other sites

  • 2 years later...

I know this is a pretty old thread, but every time it comes to creating a Repeater through the API, I get LOST.

@thetuningspoon's function illuminates a LOT of what happens behind the scenes when a new Repeater is made through the GUI - but I just keep wishing it was as simple as:

$f = new FieldtypeRepeater();
$f->set('name', 'repeaterTest');
$f->add($repeaterField1);
$f->add($repeaterField2);
$f->save();

Because that isn't technically possible without setting the Fieldgroup or Template that will be used by the Repeater first, right?

 

At the end of the day, I think we should improve the docs for Repeaters (https://processwire.com/api/fieldtypes/repeaters/) to include a section on creating a Repeater from scratch through the API so that this process is more easily understood. I am always referencing several Forum posts and parts of the docs in order to get through the spaghetti - having everything in one place would be great.

Otherwise it would be awesome to have some functions as a part of the FieldtypeRepeater Class that cut down on the amount of API calls one needs to make in order to create one of these.

 

Just my 2 cents.

  • Like 7
Link to comment
Share on other sites

  • 1 month later...
7 hours ago, elabx said:

Has anyone have any experience adding fields to Repeater Matrix items (through the API of course)? Like, add a new field to certain repeater matrix type.

Probably a question best answered by Ryan in the pro support board, but this worked for me...

// Name of repeater matrix field
$matrix_field_name = 'test_matrix';

// Name of matrix type you want to add field to
$matrix_type_name = 'type_1';

// Name of field you want to add to matrix type
$add_field_name = 'images';

// Get field objects
$matrix_field = $fields->get($matrix_field_name);
$add_field = $fields->get($add_field_name);

// Add field to repeater matrix fieldgroup
$matrix_template = $templates->get("repeater_$matrix_field_name");
$matrix_fieldgroup = $matrix_template->fieldgroup;
$matrix_fieldgroup->add($add_field);
$matrix_fieldgroup->save();

// Associate field with matrix type
$matrix_types = $matrix_field->type->getMatrixTypes($matrix_field);
$matrix_type_integer = $matrix_types[$matrix_type_name];
$property = "matrix{$matrix_type_integer}_fields";
$field_ids_in_type = $matrix_field->$property;
$field_ids_in_type[] = $add_field->id;
$matrix_field->$property = $field_ids_in_type;
$matrix_field->save();

 

  • Like 4
Link to comment
Share on other sites

  • 10 months later...
  • 1 year later...

  For me, it looks like, adding to property repeaterFields does not work correctly. Changed it to the following:

$repeaterFieldIds = [];

foreach($fieldsArray as $field) {
  $repeaterFieldIds[] = $this->fields->get($field)->id;
}

$f->repeaterFields = $repeaterFieldIds;
    
$f->save();

 

Link to comment
Share on other sites

  • 6 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Similar Content

    • By Saleena Jhon
      Hello There, I have saw a post that was covering event-calendar with php, ajax and js. That was showing a monthly overview when I click on a "month" button or when I switch the month. And show the events on one particular date when I pick a day. Also, most events are kind of exhibitions and so they have a start date and an end date much later, and occur on each day in-between as well. So on the template I put two date picking fileds date_start and date_end. Is there an elegant way to select the events using the API? If yes, kindly help me out.
      Thanks in Advance
      Regards: 
       
    • By prestoav
      Hi all,
      I could have sworn I used to be able to use the site generic 'title' field as a sub field of a repeater field. However I've tried to do this on two 3.0.165 sites recently and, while it will add the title field in the repeater field setup, it wont save the repeater title sub field's content when the repeater is used in a page template and edited.
      It;'s not a big issue but I wondered if this was a known restriction?
    • By opalepatrick
      I am working on my first Process Module. I am creating forms. Fairly straightforward. However, I really can't work out how to create multiple fieldsets?
      $fieldset = $this->modules->get('InputfieldFieldset'); $fieldset->label = 'Customer Source'; $field = $this->modules->get('InputfieldPage'); $field->inputfield = 'InputfieldSelect'; $field->findPagesSelector = 'parent_id=1449, include=hidden'; $field->labelFieldName = 'yff-lead'; $field->name = 'yfflead'; $field->columnWidth = 16; $fieldset->add($field); $field = $this->modules->get('InputfieldPage'); $field->inputfield = 'InputfieldSelect'; $field->findPagesSelector = 'parent_id=1452, include=hidden'; $field->labelFieldName = 'customer-type'; $field->name = 'customertype'; $field->columnWidth = 16; $fieldset->add($field); //Rinse and Repeat $fieldset->label = 'Contacts'; $field = $this->modules->get('InputfieldPage'); $field->inputfield = 'InputfieldSelect'; $field->findPagesSelector = 'parent_id=1538, include=hidden'; $field->labelFieldName = 'salutation'; $field->name = 'salutation'; $field->columnWidth = 16; $fieldset->add($field); I can create the first fieldset (Customer Source) but then get into trouble as the second fieldset overwrites the first. I understand why, but trying to use the open and close fieldset routine has flummoxed me. Any help appreciated.
    • By VeiJari
      Hello forum, we're trying to use Processwire as our REST-API. We are having problems with our API login to Processwire from frontend. It gives us 403 error.
      We have installed ProcessWire to subdirectory (/api/*) and our frontend is static JS files at root ( / ). Apache access logs gives 404 to our POST-request, but browser devtools shows 403 for our POST /api/login request. 
      Processwire backend panel works. We also have a GET endpoint for the API that returns 200 with correct payload.  So we're wondering why does our GET works but POST doesn't?
      Does this have something to do with Processwire .htaccess, or is this because of our webhost? What should we check first? Any help would be appreciated.
    • By rjgamer
      Hi,
      is there a hook after the current (active) page got created? Or which method got called in the Page class after the Constructor of the current page got initialized?
      Thanks.
       
×
×
  • Create New...