Jump to content
thetuningspoon

Assigning Fields to Repeaters with the API

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?

Share this post


Link to post
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.

Share this post


Link to post
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 :)

Share this post


Link to post
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();

Share this post


Link to post
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 2

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

I agree it does seem complicated, but so long as it works for you :)

On mobile at the moment, but I might try to clean up that codea little and comment it better so people know what's going on.

Glad it worked for you!

Share this post


Link to post
Share on other sites

Maybe we could put together a function that would take the repeater name and desired fields as inputs and do all the hard work of creating the fieldgroup, template, and page... If I get a few extra minutes I may give that a try.

Share this post


Link to post
Share on other sites

Exactly what I was thinking. If you don't get to it, I'll do it when I am back at my computer.

Share this post


Link to post
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 11

Share this post


Link to post
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

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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 think that will do it.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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 6

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

Is this thread still alive? Has something changed so far, so that repeater fields could be added in a more elegant way (I mean, when added programmatically)? 🙂

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By ngrmm
      I have a page with a table. Each table row has a page-reference field and a checkbox.
      The Page sends emails to all users (page-refrence->email-field) and change the value of the checkbox in a row to 1.
      It works with this:
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?> But i have to use a variable in my emailbody.inc which i'm able to get in the table-loop.
      So i do the including of the body inside my loop. But this doesn't work anymore. Page sends out the emails but is unable to change the value of the checkbox.
      I get no errors!
      I'm using ProTable
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?>  
    • By Vigilante
      In general, is there an easy way to know which method should be used to access the API?
      For example, when _ini.php is used in the theme, it would seem you have to use wire()->addHookBefore(...). But on other sites where they used ready.php, I've seen it go straight to just doing $this->addHookAfter() even though there is no class or namespace set up in the ready.php file.
      So how do I know when I can do $this, or $wire, or wire() to access things? And any other variables I'm not aware of.
      Thanks!
    • By VeiJari
      Hello forum,
      This is really a weird one, because front end editing works in a earlier website we did to a customer. 
      When I check the source code for current website it does initiate front end edit: 
      <span id=pw-edit-1 class='pw-edit pw-edit-InputfieldPageTitle' data-name=title data-page=1021 data-lang='1017' style='position:relative'><span class=pw-edit-orig>Tekijät</span><span class=pw-edit-copy id=pw-editor-title-1021 style='display:none;-webkit-user-select:text;user- select:text;' contenteditable>Tekijät</span></span>  But when I double click nothing happens (yes I'm 100% sure I'm superuser and logged in)
      I also tried to apply the front end with other methods than:
      $page->edit('title'); But didn't work either.
      We are using jquery 2.2.4, so it should not be a problem.
      Is this a bug related to current master or something else?
      Someone else having this problem as well?
    • By jom
      Hi everyone
      It seems that I don't fully understand the wireTempPath() function and I need some help.
      I use wireTempPath() to create a new location in assets/cache/WireTempDir and than copy a pdf from the assets/files/page folder to the new folder. I want the file to be accessible only for a limited time, that's why I use wireTempPath.
      The file seems to be copied to the right location, but gets deleted right afterwards, according to 
      As mentioned in the topic above, 
      $wireTempDir->setRemove(false); prevents the file to be deleted. But I like the file to be automatically deleted after a few days. So, how can I do that?
      My code so far (everything works, but the automatic removal of the tempDir folder):
      //generate and show download link $folder = time(); // timestamp as temporary folder $maxAge = (int) $settings->options_downloadlink_valid_hours * 3600; //tempDir wants maxAge as seconds $options = array( 'maxAge' => $maxAge ); $wireTempDir = wireTempDir($folder, $options); $wireTempDir->setRemove(false); $src_file = $page->ebook_download->filename; // Create a new directory in ProcessWire's cache dir if(wire('files')->mkdir($wireTempDir, $recursive = true)) { if(wire('files')->copy($src_file, $wireTempDir)){ //get subdirs from tempDir: $pos = strpos($wireTempDir, "WireTempDir"); $subdir = substr($wireTempDir, $pos, 100); $out .= "<p><a href='" . wire('pages')->get('template=passthrough')->httpUrl . "?file=" . $subdir . $page->ebook_download->basename . "' target='_blank'>$page->title</a></p>"; } } I appreciate any ideas - thanks!
      Oliver
    • By VeiJari
      Hello forum, this is my first security related post, so I'm a bit of a newbie.
      I understand that when I have direct front-input from user I should sanitize the input, but how about when I use a secret key for showing a API for a third-party supplier? Should I sanitize the input->get() key?
      I've tested this issue and I tried ?key=<?php echo $page->field; ?> And without adding any sanitization it comes back: /?key=<?php%20echo%20$page->field;%20?>
      So can I rely on this, or should I still use $sanitizer just in case?
       
      Thanks for the help!
×
×
  • Create New...