When editing a location with custom map markers available, the custom marker is not saved.

Steps To Reproduce

  1. Make sure the Experience add on is active.
  2. Edit an existing location.
  3. Change the map marker via the map marker selector, a built-in marker is fine.
  4. Save the location.

The map marker is not changed or saved to the database.

Resolution

Have \SLP_Admin_Locations_Actions::save_edited_location to call \SLP_Data_Extension::set_cols(true) after reading the pre-existing record in order to force the valid extended data fields to the full array.

Other Related Issues

Disappearing Location Marker Interface On Edit

  • Experience is active
  • Edit location save
  • Edit location again
  • Experience is active
  • Edit location save
  • Click Locations on the sidebar (to reload locations interface)
  • Edit location again
Entire added meta for Experience location data goes away after first save.

The fix for extended meta data fixed this as well.

Research

Findings

  • The activation class is firing the update request (λ \SLP_Experience_Activation::update) properly.
  • λ \SLP_Experience_Activation::add_extended_data_fields is firing the following methods properly:
    • λ \SLP_Data_Extension::add_field
    • λ \SLP_Data_Extension::update_data_table

The problem…

  • \SLP_Admin_Locations_Actions::save_edited_location
    • First loads existing location data via \SLPlus_Location::set_PropertiesViaDB
      • Which eventually calls \SLPlus_Location::set_PropertiesViaArray looping through all DB data and calls…
        • \SLPlus_Location::__set which sets the exdata array

At this point the \SLPlus_Location::exdata array is set with the ORIGINAL data values.
The marker key is not present.

Now when we run through our $_POST values…

  • \SLPlus_Location::valid_location_property eventually calls
    • \SLP_Data_Extension::set_cols WITHOUT the force flag
      • The metatable[‘records’] already has values and thus this process is skipped
  • And at this point the valid_location_property returns false on ‘marker’ as a valid field

Extended Data Meta

No edits, just click Store Locator Plus® | Locations

Ω \SLPlus_Location

Φ \SLPlus_Location::$exdata
λ \SLPlus_Location::valid_location_property
phpDoc
	/**
	 * Return true if the property is valid.
	 *
	 * @param string $property property name to validate
	 *
	 * @return boolean true if property is OK
	 */

In this scenario, $property = ‘marker’
Φ \SLPlus_Location::$exdata has only 3 keys ‘contact’, ‘first_name’, ‘last_name’
So it then runs…
λ \SLP_Data_Extension::set_cols
Which returns the same 3 columns…


Ω \SLP_Data

λ \SLP_Data::is_Extended
phpDoc…
	/**
	 * Determine if the data is extended based on whether the associated table has rows.
	 *
	 * @return bool True if the table has rows and data is extended, false otherwise.
	 */

$this->options[‘data_is_extended’] = TRUE
$options is a local private array of \SLP_Data that has two properties both start as null:
data_is_extended – set by checking table_has_rows on the extended data table
has_extended_data – true (at least 1 record in extended data table)


Ω \SLP_Data_Extension

λ \SLP_Data_Extension::add_field
phpDoc…
/**
	 * Adds a field to the data table, if already exits then update it.
	 *
	 * mode parameter
	 * - 'immediate' = default, run create table command when adding the field
	 * - 'wait' = do not run the create table command when adding this field
	 *
	 * @param string $label Plain text field label for this field.
	 * @param string $type The data type for this field.
	 * @param array $options Options field contains serialized data for this field.
	 *
	 * @key    string    'slug'                Unique field slug
	 * @key    string    'addon'                Slug for the add-on object (short slug i.e. 'slp-experience')
	 * @key    string    'display_type'        Display helper used to help with admin and UI rendering.
	 *                                           'checkbox'
	 *                                           'icon'          the icon picker, with a text input box set to the field value
	 *                                           'input'|'text'  for a single line input
	 *                                           'none'          do not render this field
	 *                                           'subheader'     a h3 label
	 *                                           'textarea'      for a multiline textarea input
	 *                                           'submit'        A submit button with value set to the field value
	 * @key    string  'help_text'         The help text to display next to the field.
	 *
	 * @param string $mode 'wait' or 'immediate' determines if we call the update table structure as soon as this is called.
	 *
	 * @return string the slug of the field that was added.
	 */
λ \SLP_Data_Extension::get_active_cols
phpDoc
	/**
	 * Get the active columns (those for active add-on packs.
	 *
	 * @return stdClass[] columns for active plugins.
	 */
λ \SLP_Data_Extension::set_cols
phpDoc
	 * Reads the metadata from the slp_extendo_meta table as OBJECTS and stores it in metatable['records'][<slug>]
	 *
     * @used-by \SLPlus_Location::valid_location_property
     * @used-by \SLP_Data_Extension::get_cols
     * @used-by \SLP_Data_Extension::get_active_cols
     * @used-by \SLP_Admin_Locations_Actions::save_edited_location
     *
	 * @param boolean $force = set true to force reloading of data.
Φ \SLP_Data_Extension::$metatable

The metatable property contains the meta that is stored in the wp_slp_extendo_meta data table.
This table defines the extra data fields in the SLP data set that are tied to locations.
It contains the field names, labels, and properties for those fields.

  • name = ‘wp_slp_extendo_meta’
  • records = array of the extended data fields (records from wp_slp_extendo_meta)
    • $this->metatable[‘records’][‘marker’] = stdClass with slug=’marker’

wp_slp_extendo_meta
Current debug state of the wp_slp_extendo meta table shows the marker field does exist

Storing Data

Ω \SLPlus_Location

λ \SLPlus_Location::MakePersistent
$_POST[“marker”] is set to the new fully qualified marker URL

i.e. http://localhost/wp-content/plugins/store-locator-plus/images/icons/bulb_clear_azure.png

$dataToWrite does NOT have a key named ‘marker’

This runs $this->dbFields through \SLPlus_Location::mapPropertyToField via array_reduce

(\SLPlus_Location) $this->exdata does NOT have a key named ‘marker’

$this is an instantiation of \SLPlus_Location containing the properties related to the current location being manipulated.

$this->exdata is set via the __set magic method.

λ \SLPlus_Location::valid_location_property
phpDoc
	/**
	 * Return true if the property is valid.
     *
     * @used-by \SLPlus_Location::set_PropertiesViaArray
     * @used-by \SLP_Admin_Locations_Actions::add_location
     * @used-by \SLP_Admin_Locations_Actions::save_edited_location
	 *
	 * @param string $property property name to validate
	 *
	 * @return boolean true if property is OK
	 */

Activation/Updates

λ \SLP_Experience_Activation::update

appears to be fired via an automation mechanism via this property:
\SLP_BaseClass_Addon->activation_class_name

\SLP_BaseClass_Addon->activation_class_name is reviewed by \SLP_BaseClass_Admin::update_prior_installs
This is fired whenever the plugin version updates (updating the version of wp-content/plugins/slp-experience.php to a newer version does fire this in our test case)

λ \SLP_BaseClass_Admin::update_prior_installs

	/**
	 * Update prior add-on pack installations.
	 */

$this->addon->activation_class_name = ‘SLP_Experience_Activation’
$classToActivate = ‘SLP_Experience_Activation’ which does NOT yet exist

Since the class name is old-school it uses the legacy loader method:
file_exists( $this->addon->dir . ‘include/class.activation.php’
require_once( $this->addon->dir . ‘include/class.activation.php’ );
$this->activation = new $classToActivate( [ ‘addon’ => $this->addon ] );

That class now exists and is an instance of SLP_Experience_Activation
Calling \SLP_Experience_Activation::update



When calling the add_field, has_field returns TRUE that ‘marker’ already exists.. $slug is ‘marker’

if ( ! $this->has_field( $slug ) ) {


Tasks

  • Look into ‘old_slug’ in \SLP_Data_Extension
  • Look at \SLP_Data_Extension::set_cols and see about the force flag

Glyphspeak Symbols

SymbolMeaning
Ω – omegaclass
λ – lambdamethod/function
Φ – uppercase phifield/property
– black square (unicode)persistent data store
	•	ΩName — class declaration
	•	Ω ⊢ Φ•x — property in class
	•	Ω ⊢ λ•do() — method in class
	•	Ξ ∈ Ω — instance Ξ belongs to class Ω
	•	Ω⁰ — abstract/base class
	•	ΩΔ — derived/subclass
	•	ΩΣ — sealed/final class

	•	■ ⊢ λ• — method declared on class ■
	•	■.m ⟶ λ• — name → method mapping
	•	self ⟼ λ• — instance binding indicator

	•	■ ⊢ Φ•name:τ — property name of type τ on class ■
	•	Φ•name↑ — getter-only (read-only)
	•	Φ•name↕︎ — getter + setter
	•	Φ•name! — constant/immutable after init
	•	Φ•name∅ — nullable/optional
	•	Φ•name⟲ — observable/reactive property
	•	Φ•name := v₀ — default/initial value
	•	Φ□ — class/static property (no instance dot)
	•	⊘Φ•name — private/hidden property

	•	■Φ•name → persisted property
	•	Ω ⟷ ■ → class ↔ data store
	•	Ξ⟪■⟫ → instance serialized to vault

🧩 AI Summary (Glyphspeak Protocol)

ΞScope: MySLP SaaSAttachment.tiff ⊕ SLP WordPress PluginsAttachment.tiff

ΞIssue: Custom map markers were not saving when users edited existing locations whose extended data fields (e.g., marker_url) were initially null.

ΔDiagnosis:

save_edited_location(λ) relied on previously loaded data and skipped updating null fields. This prevented the Experience plugin’s extended data (custom markers, icons) from persisting when newly added.

🩹Resolution:

Forced save_edited_location(λ) to reload extended data columns immediately after fetching an existing record. Ensures that null fields can now transition to non-null values correctly.

✅Outcome:

  • Marker changes now persist for both new and existing locations.
  • Verified across SaaS and WordPress builds.
  • Null-to-value transitions now save properly.
  • Regression risk: minimal.

🎴Insight:

Revalidates the need for full record reloads before save operations when using multi-layered data contexts (base + extended tables). Prevents stale cache writes.

Leave a Reply