Jump to content

Trying to add add'l Fieldtype support to ImportPagesCSV (MapMarker & Repeater; Page ✓)


hellomoto
 Share

Recommended Posts

Resources (Ryan's own):

So I edit the accepted $fieldtypes on :76:

	/**
	 * List of Fieldtypes that we support importing to
	 *
	 */
	protected $fieldtypes = array(
		'FieldtypePageTitle',
		'FieldtypeText',
		'FieldtypeTextarea',
		'FieldtypeInteger',
		'FieldtypeFloat',
		'FieldtypeEmail',
		'FieldtypeURL',
		'FieldtypeCheckbox',
		'FieldtypeFile',
		'FieldtypePage',
		'FieldtypeMapMarker',
		'FieldtypePassword',
		'FieldtypeRepeater'
		);

Page, MapMarker, Password and Repeater were added by me. 

Then alter importPageValue:

	/**
	 * Assign a value to a page field 
	 *
	 */
	protected function importPageValue(Page $page, $name, $value) {

		$field = $this->fields->get($name); 

		if($field->type instanceof FieldtypeFile) {

			$value = trim($value); 
			// split delimeted data to an array
			$value = preg_split('/[\r\n\t|]+/', $value); 
			if($field->maxFiles == 1) $value = array_shift($value);
			$data = $page->ImportPagesCSVData; 
			$data[$name] = $value; 
			$page->ImportPagesCSVData = $data; 

		} elseif($field->type instanceof FieldtypePage) {

			$value = trim($value);
			if(wire("pages")->find("$name=$value")) $page->set($name, $value);

		} elseif($field->type instanceof FieldtypeMapMarker) {

			$value = trim($value);
			$page->set($name->address, $value);

		} elseif($field->type instanceof FieldtypeRepeater) {

			//

		} else { 

			$page->set($name, $value); 
			if($name == 'title') $page->name = $this->sanitizer->pageName($value, 2); // Sanitizer::translate
			elseif($name == 'fullname') {
				$page->name = $this->sanitizer->pageName($value, true);
			} 
		}
	}

Page import works with ID values, which was trivial to incorporate; passwords too. MapMarker and Repeater as you might guess do not. 

How can I save the map->address value? Hopefully it will update the corresponding map fields too but one thing at a time.

As for the repeaters... LostKobrakai tipped me off to 

foreach($page->engines as $e) {
    foreach($e->fields as $field) {
        echo $field; echo $e->get($field);
    }
}

which works for their names and values, but in this function you're passed the field, and something like


foreach($page->$field as $e) {
    foreach($e->fields as $field) {
    	echo $field;
        echo $e->get($field);
    }
}

doesn't work... and what it would need to do inside anyway is check for a subfield whose name is equal to the column header (choose the repeater field itself e.g., engines in the select per repeater subfield value, e.g., engine_fueltype), then explode that cell value by pipes ('|'), and for each subvalue, populate the repeater#->subvalue... but before all that I need to be able to iterate through the subfields from the field in this function. 

Anyone have any ideas?

Link to comment
Share on other sites

Or the repeater subfields could be added to the select list for the field connections directly... that would simplify the importPageValue part.

But still... if I do 

	/**
	 * Build the "Step 2" form to connect the fields
	 *
	 */
	protected function buildForm2() {

		$form = $this->modules->get("InputfieldForm"); 
		$form->method = 'post';		
		$form->action = './'; 
		$form->description = "Step 2: Connect the fields"; 
		$form->value = 
			"Below is a list of fields found in your CSV file. " . 
			"For each of them, select the field it should import to. " . 
			"Leave any fields you want to exclude blank. " . 
			"Once finished, click 'Start Import' at the bottom of this page. " . 
			"Note: any field names in your CSV file that match those in your site " . 
			"will be automatically selected.";

		$fp = fopen($this->csvFilename, "r"); 
		$data = fgetcsv($fp, 0, $this->session->csvDelimeter, $this->session->csvEnclosure);

		foreach($data as $key => $value) {

			$f = $this->modules->get("InputfieldSelect"); 
			$f->name = "csv" . $key;
			$f->label = $value; 
			$f->addOption(''); 

			foreach($this->template->fieldgroup as $field) {
				$valid = false;

				foreach($this->fieldtypes as $ft) {
					if($field->type instanceof $ft) {
						$valid = true;
						break;
					}
				}

				if(!$valid) continue; 

				if($field->type instanceof FieldtypeRepeater) {
					echo count($field->repeaterFields);
					foreach($field->repeaterFields as $item) {
						echo wire("fields")->get($item)->name . '<br/>';
						//$label = wire("fields")->get($item);
						//$label = $item->name;
						//$f->addOption($item->name);
					}
				}

				$label = $field->name; 
				$f->addOption($field->name, $label); 
				if($field->name == $value) $f->attr('value', $field->name); 
			}

			$form->add($f);
		}

		fclose($fp); 

		$this->addSubmit($form, 'Start Import');

		return $form;
	}

That returns 8 for count($field->repeaterFields), which is correct. Yet, executing the next foreach statement echoes 23 iterations of name of each subfield. For example: repeater field = "engines", its repeaterFields = ["engine_field1", "engine_field2", "engine_field3", "engine_field4", "engine_field5", "engine_field6", "engine_field7", "engine_field8"] and each of those are echoed out together -- 23x. Should just be once, for a total of 8 lines, not 184...

Link to comment
Share on other sites

The repeater field value should be a pageArray, with all the repeater pages. Problem with repeaters, you're only coping fields right now. But repeaters are own hidden pages, which have to be generated before you can copy over the fields. Have a look here how to handle repeaters from the api side: http://processwire.com/api/fieldtypes/repeaters/

} elseif($field->type instanceof FieldtypeRepeater) {
  foreach($value as $e){
    …
  }
}

For the map marker: Did you take a look what's being returned by $value? I doubt it's a string so $value = trim($value); doesn't do a thing. Try this, as $value should carry address as part of it, after a quick look at the source of the module.

$page->set($name, $value);
Link to comment
Share on other sites

I know repeaters are pages... For my main template that I am trying to get this to work for primarily, I have a repeater field engines with the default ready set to 2. So I would assume that these are created nonetheless when creating a page of that template via the API... This is tricky. I think perhaps the individual repeater fields should be included in the fields list when matching them prior to actually importing and then separate the values into repeaters on import? 

Anyway I think I'll try to get the mapmarker working first. The $value in importPageValue isn't what's returned by MapMarker, it's the value you're setting for the field, from the CSV.

Link to comment
Share on other sites

  • 6 months later...

Great mod. But importing Map Marker didn't work for me. Could anyone help please?

Also posted this here:

https://processwire.com/talk/topic/383-module-import-pages-from-csv-file/page-9

I see that you aren't following either topic so not sure if you will see these responses, but I posted a detailed response about how you can use Batch Child Editor for importing CSV files into pages with MapMarker fields in response to your post in the other thread. Hope that helps.

Link to comment
Share on other sites

Reading docs may really help sometimes...

Quote from the @hellomoto's code:

elseif($field->type instanceof FieldtypeMapMarker) {
 
$value = trim($value);
$page->set($name->address, $value);
 
}

And now some code from Map Marker docs ( https://processwire.com/talk/topic/9711-map-marker-map/ ):

$page->marker->address = 'Disney Land';

Now it's obvious that Hellomoto's code should be like this:

elseif($field->type instanceof FieldtypeMapMarker) {
 
$value = trim($value);
$page->$name->address = $value;
 
}

Tried this, works like a charm. Lat and Lng subfields are generated automagically if the address is more or less correct.

Thanks @Hellomoto for a great mod! @Ryan, maybe it's time to add this to Your original module?

Link to comment
Share on other sites

  • 1 year later...
  • 2 months later...
On 7/23/2015 at 4:26 PM, theoretic said:

Reading docs may really help sometimes...

Quote from the @hellomoto's code:

elseif($field->type instanceof FieldtypeMapMarker) {
 
$value = trim($value);
$page->set($name->address, $value);
 
}

And now some code from Map Marker docs ( https://processwire.com/talk/topic/9711-map-marker-map/ ):

$page->marker->address = 'Disney Land';

Now it's obvious that Hellomoto's code should be like this:

elseif($field->type instanceof FieldtypeMapMarker) {
 
$value = trim($value);
$page->$name->address = $value;
 
}

Tried this, works like a charm. Lat and Lng subfields are generated automagically if the address is more or less correct.

Thanks @Hellomoto for a great mod! @Ryan, maybe it's time to add this to Your original module?

This is true, and is what I have, but it's not working in another custom import script I'm working on currently:

function importCSV($filepath, $template, $parent_id = null, $grandparent_id = null) {
  $csv = array_map('str_getcsv', file($filepath));
  array_walk($csv, function(&$a) use ($csv) {
    $a = array_combine($csv[0], $a); # set header keys
  });
  array_shift($csv); # remove column header
  //echo '<pre>'; print_r($csv); echo '</pre>';
  foreach($csv as $r) {
    $p = new Page();
    $p->name = wire('sanitizer')->pageName($r['title']);
    $p->template = $template;
    if($parent_id !== 0||null) {
      $p->parent_id = $parent_id;
    } elseif($parent_id == 0||null) {
      //echo $r['parent'].' ';
      $parent = wire('sanitizer')->pageName($r['parent']);
      $parent = str_replace('---','-',$parent);
      //echo $parent.' ';//echo $grandparent_id.'gp ';
      $parent = wire('pages')->get('title=' . $r['parent'] . ', parent_id=' . $grandparent_id)->id;
      //echo $parent.'p ';
      $p->parent_id = $parent;
      unset($r['parent']);
    }
    $p->save();
    $p->of(false);
    foreach($r as $k=>$v) {
      $v = trim($v);
      $fieldtype = wire('fields')->get('name='.$k)->type;
      if($fieldtype = 'FieldtypeImage') {
        $imgs = explode('|', $v);
        foreach($imgs as $i) {
          $p->$k = $i;
        }
      }
      elseif($fieldtype = 'FieldtypeMapMarker') {
        $p->set($k->address, $v); // $p->$k->address = $v;
      }
      else $p->$k = $v;
    }
    $p->save(); //echo '<br>';
  }
}

I have tried the way you mention here that's commented out in my code as well, to no avail.

Edit: It should be $p->$k->set('address', $v);

Edited by hellomoto
Link to comment
Share on other sites

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

×
×
  • Create New...