Viewing 6 reply threads
  • Author
    Posts
    • #9463
      joezim
      Participant

      Here’s my old version of a settings page:

      <?php
      /*
      Setting: reciprocity_changelog
      Order: 1
      */
      
      $states = \App\Model\ReciprocityStates::getStates();
      
      piklist('field', array(
      	'type' => 'group',
      	'field' => 'changes',
      	'label' => 'Updates',
      	'list' => false,
      	'description' => 'A grouped field with the field parameter set.',
      	'add_more' => true,
      	'fields' => array(
      		array(
      			'type' => 'date',
      			'field' => 'date',
      			'label' => 'Date',
      			'columns' => 2
      		),
      		array(
      			'type' => 'select',
      			'field' => 'state',
      			'label' => 'State',
      			'columns' => 4,
      			'choices' => array_combine($states, $states)
      		),
      		array(
      			'type' => 'text',
      			'field' => 'description',
      			'label' => 'Description',
      			'columns' => 6
      		)
      	)
      ));

      It simply has a repeater for 3 fields. The problem is that there are 826 (and growing) items in that array, so the page takes around a minute to run the JS and convert the fields to repeater rows, etc. So I decide to manually build the form to be simpler, like this:

      
      <?php
      /*
      Setting: reciprocity_changelog
      Order: 1
      */
      
      $states = \App\Model\ReciprocityStates::getStates();
      $updates = \App\Model\ReciprocityData::getReciprocityUpdates();
      
      ?>
      
      <div class="new_log">
      	<p>Fill out the fields below and click "Add" to add a new update.</p>
      
      	<div class="field-row">
      		<label class="field">
      			<span class="field-label">Date</span>
      			<input type="date" name="reciprocity_changelog[changes][<?= count($updates) ?>][date]" class="field-input">
      		</label>
      		<label class="field">
      			<span class="field-label">State</span>
      			<select name="reciprocity_changelog[changes][<?= count($updates) ?>][state]" class="field-input">
      				<option value="">Select a State</option>
      				<?php foreach ($states as $state) : ?>
      					<option value="<?= $state ?>"><?= $state ?></option>
      				<?php endforeach ?>
      			</select>
      		</label>
      		<label class="field field--wide">
      			<span class="field-label">Description</span>
      			<input type="text" name="reciprocity_changelog[changes][<?= count($updates) ?>][description]" class="field-input description-field">
      		</label>
      	</div>
      
      	<button type="submit" name="submit" id="submit" class="button button-primary">Add</button>
      </div>
      
      <?php foreach ($updates as $i => $update) : ?>
      	<?php if ($i < 10) : // Only make the latest 10 editable ?>
      
      		<div class="field-row">
      			<label class="field">
      				<span class="field-label">Date</span>
      				<input type="date" name="reciprocity_changelog[changes][<?= $i ?>][date]" value="<?= $update['date'] ?>" class="field-input">
      			</label>
      			<label class="field">
      				<span class="field-label">State</span>
      				<select name="reciprocity_changelog[changes][<?= $i ?>][state]" class="field-input">
      					<?php foreach ($states as $state) : ?>
      						<option value="<?= $state ?>" <?= $state == $update['state'] ? 'selected' : '' ?>><?= $state ?></option>
      					<?php endforeach ?>
      				</select>
      			</label>
      			<label class="field field--wide">
      				<span class="field-label">Description</span>
      				<input type="text" name="reciprocity_changelog[changes][<?= $i ?>][description]" value="<?= $update['description'] ?>" class="field-input description-field">
      			</label>
      		</div>
      
      	<?php else : ?>
      
      		<input type="hidden" name="reciprocity_changelog[changes][<?= $i ?>][date]" value="<?= $update['date'] ?>">
      		<input type="hidden" name="reciprocity_changelog[changes][<?= $i ?>][state]" value="<?= $update['state'] ?>">
      		<input type="hidden" name="reciprocity_changelog[changes][<?= $i ?>][description]" value="<?= $update['description'] ?>">
      
      	<?php endif ?>
      <?php endforeach ?>
      ?>

      I’m pulling in the data and building the form fields manually. Only the first 10 are visible and editable, the rest are hidden fields to make the page more performant (note, when I call getReciprocityUpdates it orders the updates by date, so it will always have the latest 10 on top). There’s also a set of fields at the top to append another row at the end of the array (note the [count($updates)] to add the index number in the field name). The field names are exactly the same, as far as I can tell, as the ones generated by piklist in the original version, but when I click the submit button the data doesn’t update. It says “Settings Saved” at the top, but no change was made to the DB. What am I missing?

    • #9467
      joezim
      Participant

      Nevermind. I figured it out (I believe. I haven’t actually finished fixing it and testing to see it work). Turns out it wasn’t working with the Piklist version either. With so many entries, I was running up against the POST request size limit for my server, so the data was getting truncated. The truncation was causing it to fail validation so Piklist was saying to use the old value.

    • #9468
      Steve
      Keymaster

      You may also run into a rendering issue with the browser. We’ve seen that happen.

      800+ repeater fields is probably not the best user experience. Some suggestions:
      1) Break them up under different Workflow tabs:
      https://piklist.github.io/docs/tutorials/workflows/building-first-workflow/

      2) Use a Post Type instead, and have 800+ posts.

      Good luck!

    • #9469
      joezim
      Participant

      Yeah, I’m breaking them up. There are 52 “tabs” because they are broken up for each of the 50 US States plus Washington DC and New York City, so literal tabs don’t make sense because there would be so many, and *workflow* tabs *really* don’t make any sense for this. I considered a post type, but each entry only has 3 tiny bits of info, so it doesn’t make much sense to bring in the complexities of a custom post type. I’m going to have a “Main” page with a link to each of the 52 “states” and just list out all of them for that state to be editable and have a box at the top of the page for adding a new entry. It’ll be a lot simpler and it should be many years before we run into any types of size limitations.

      Thanks.

    • #9470
      Steve
      Keymaster

      I’d love to see it. Send over screenshots if you can.

      Steve

    • #9473
      joezim
      Participant

      Split the states up:
      https://screencast.com/t/fWIZ5oDqNZ

      Each state:
      https://screencast.com/t/VIvtKErBi

      Here’s the code under piklist/parts/settings/changelog.php

      <?php
      /*
      Setting: reciprocity_changelog
      Order: 1
       */
      
      use \App\Model\ReciprocityData;
      use \App\Model\ReciprocityStates;
      
      $selectedState = $_GET['state'];
      $states = ReciprocityStates::getStates();
      
      /**
       * SECTION 1: CHOOSE A STATE
       */
      
      if (!$selectedState || empty($selectedState)):
      ?>
      
      <h1>Choose A State</h1>
      
      <div class="state-selector">
      	<?php foreach ($states as $state): ?>
      		<?php $stateAbbreviation = ReciprocityStates::getLowercaseStateAbbreviation($state);?>
      		<a href="<?=admin_url('edit.php?post_type=reciprocity-pages&page=changelog&state=' . $stateAbbreviation)?>" class="state-selector__option"><?=$state?></a>
      	<?php endforeach?>
      </div>
      <style>
      	.state-selector {
      		columns: 4;
      	}
      
      	.state-selector__option {
      		display: block;
      		font-size: 1.5em;
      		line-height: 2;
      	}
      
      	#submit {
      		display: none;
      	}
      </style>
      
      <?php
      /**
       * SECTION 2: STATE IS CHOSEN SO SHOW UPDATES
       */
      else:
      	$selectedStateName = ReciprocityStates::getStateNameFromAbbreviation($selectedState);
      	$updates = ReciprocityData::getReciprocityUpdates($selectedStateName);
      ?>
      
      <h1><?=$selectedStateName?> Changelog</h1>
      
      <p class="changelog-note">Changelog descriptions allow Markdown, which allow links in the following format:<br><code>Read the [blog post](https://www.usconcealedcarry.com/link-to-blog-post).</code></p>
      
      <div class="new_log">
      	<p>Fill out the fields below and click "Add Update" to add a new update.</p>
      
      	<div class="field-row">
      		<label class="field">
      			<span class="field-label">Date</span>
      			<input type="date" name="reciprocity_changelog[<?=$selectedStateName?>][<?=count($updates)?>][date]" class="field-input">
      		</label>
      		<label class="field field--wide">
      			<span class="field-label">Description</span>
      			<input type="text" name="reciprocity_changelog[<?=$selectedStateName?>][<?=count($updates)?>][description]" class="field-input description-field">
      		</label>
      	</div>
      
      	<button type="submit" name="submit" id="submit" class="button button-primary">Add Update</button>
      </div>
      
      <?php
      foreach ($updates as $i => $update): ?>
      	<div class="field-row">
      		<label class="field">
      			<span class="field-label">Date</span>
      			<input type="date" name="reciprocity_changelog[<?=$selectedStateName?>][<?=$i?>][date]" value="<?=$update['date']?>" class="field-input">
      		</label>
      		<label class="field field--wide">
      			<span class="field-label">Description</span>
      			<input type="text" name="reciprocity_changelog[<?=$selectedStateName?>][<?=$i?>][description]" value="<?=$update['description']?>" class="field-input description-field">
      		</label>
      	</div>
      <?php endforeach?>
      
      <style>
      	.changelog-note {
      		background: #def;
      		border: 1px solid #69b;
      		border-radius: 5px;
      		padding: 15px;
      	}
      
      	.new_log {
      		background: #fafafa;
      		border: 1px solid #ddd;
      		border-radius: 5px;
      		padding: 15px;
      		margin: 15px 0;
      	}
      
      	.field-row {
      		font-size: 16px;
      		display: flex;
      		padding-bottom: 15px;
      	}
      
      	.field {
      		flex: 1;
      		padding-right: 15px;
      	}
      
      	.field:last-child {
      		padding-right: 0;
      	}
      
      	.field--wide {
      		flex-grow: 5;
      	}
      
      	.field-label {
      		font-weight: bold;
      		display: block;
      		padding-bottom: 5px;
      		padding-left: 5px;
      	}
      
      	.field-input {
      		font-size: inherit;
      		height: 2em !important;
      		padding-left: 10px !important;
      		padding-right: 10px !important;
      		width: 100%;
      		line-height: inherit !important;
      	}
      
      	.description-field {
      		min-width: 500px;
      	}
      </style>
      
      <?php endif?>
      
      <!-- STYLES FOR BOTH SECTIONS -->
      <style>
      	#wpbody-content h2 {
      		display: none;
      	}
      </style>

      And the pre_update_option_xx filter:

      function changelogUpdateFilter($new, $old) {
      	// Remove Piklist's default filter. It doesn't work for our needs.
      	remove_filter('pre_update_option_reciprocity_changelog', array('piklist_setting', 'pre_update_option'), 10);
      
      	$returnVal = $old;
      
      	foreach ($new as $state => $updates) {
      		// Filter out "Empty" results (submitted form without adding a new entry)
      		$updates = array_filter($updates, function ($update) {
      			return !empty($update['date']) && !empty($update['description']);
      		});
      
      		// Make sure the updates are in new->old order
      		usort($updates, function ($a, $b) {
      			$a = $a['date'];
      			$b = $b['date'];
      
      			if ($a == $b) {
      				return 0;
      			}
      			return ($a > $b) ? -1 : 1;
      		});
      
      		// Add new entries to the rest of the changelog by replacing all of the state's update with what was submitted,
      		// since previous updates to that state are included in the form submission.
      		$returnVal[$state] = $updates;
      	}
      
      	return $returnVal;
      }
    • #9474
      Steve
      Keymaster

      That is a very cool way to solve that issue. A real creative use of Piklist. Good job!

Viewing 6 reply threads
  • The topic ‘Manual Form Fields for Settings Page’ is closed to new replies.