Jump to content
Gadgetto

[SOLVED] What exactly are module properties and how to handle them?

Recommended Posts

Hello there,

I'm currently confused about what exactly module properties are and how to change them from outside of the module.

If I understand right I can set a new module property with the set() method. The property then internally lands in the "data" array of the module object. If you have a look at the simple module below I set two properties in class constructor. In the init() method I do something with the two properties and the execute method returns the result.

This works as expected. If I now try to change the properties from outside of MyModule the changes aren't reflected when the execute method is called (please see other source below).

 

<?php namespace ProcessWire;

class MyModule extends Process {

    public static function getModuleInfo() {
        return array(
            'title' => "My Module",
            'version' => 1,
        );
    }

  	protected $fullName = '';
  
    public function __construct() {
        $this->set('firstName', 'Han');
        $this->set('lastName', 'Solo');

    }

    public function init() {
        $this->fullName = $this->firstName . ' ' . $this->lastName;
    }

    public function ___execute() {
        return $this->fullName;
    }
}

 

class MyOtherModule extends Process {
    public static function getModuleInfo() {
        return array(
            'title' => "My Other Module",
            'version' => 1,
        );
    }

    public function ___execute() {
        $modules = $this->wire('modules');

        // Load MyModule module
        if ($modules->isInstalled('MyModule')) {
            $mymodule = $this->wire('modules')->get('MyModule');
        } else {
            $this->error($this->_('MyModule module is not installed!'));
        }
        if (!$mymodule) return '';

	    // Change properties in MyModule (is this OK here?)
        $mymodule->firstName = 'Luke';
        $mymodule->lastName = 'Skywalker';

		return $mymodule->execute();
    }
}

 

What is the problem here?

Could it be I completely misunderstand how module properties are handled?

Share this post


Link to post
Share on other sites

Modules are not different from PHP classes.

To change the properties of MyModule class from within MyOtherModule class, you can either:

  1. Make MyOtherModule class extend MyModule. It will thus inherit MyModule's public and protected properties and methods
  2. Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule's constructor method.
Edited by kongondo

Share this post


Link to post
Share on other sites
49 minutes ago, Gadgetto said:

What is the problem here?

The properties are set as protected.

You need public accessible setter method(s) like with every PHP class. Something like this:

public function setMyName($val) {
	$this->myName = $this->sanitizer->text($val);
}
public function __set($var, $val) {
	if(isset($this->$var)) $this->$var = $this->sanitizer->text($val);
}

Theoretically also direct public accessible properties can be set directly from the outside, but this is bad code, a NoGo.

Share this post


Link to post
Share on other sites

I built my samples exact the same way as in the following core module (ProcessPageSearch):

ProcessPageSearch instantiates ProcessPageLister and sets some properties for PageLister the same way I did above. What is the difference?

https://github.com/processwire/processwire/blob/649d2569abc10bac43e98ca98db474dd3d6603ca/wire/modules/Process/ProcessPageSearch/ProcessPageSearch.module#L210

Share this post


Link to post
Share on other sites

Please look into processlister.module public function set and the same function of it parents.  (I'm on mobile)

Share this post


Link to post
Share on other sites
7 hours ago, horst said:

Please look into processlister.module public function set and the same function of it parents.  (I'm on mobile)

This is the set()  method of ProcessPageLister.module class - basically nothing special

	public function set($key, $value) {
		if($key == 'openPageIDs' && is_array($value)) {
			$this->openPageIDs = $value; 
			return $this; 
		} else if($key == 'parent' && !$value instanceof Page) {
			$value = $this->wire('pages')->get($value); 
		} else if($key === 'finalSelector') {
			$this->finalSelector = $value;
		}
		return parent::set($key, $value);
	}

	public function get($key) {
		if($key === 'finalSelector') return $this->finalSelector;
		return parent::get($key);
	}

The parent class "Process" has only a get() method:

public function get($key) {
    if(($value = $this->wire($key)) !== null) return $value; 
    return parent::get($key); 
}

The parent class of Process is "WireData" - this is the set() method of "WireData":

	/**
	 * Set a value to this object’s data
	 * 
	 * ~~~~~
	 * // Set a value for a property
	 * $item->set('foo', 'bar');
	 * 
	 * // Set a property value directly
	 * $item->foo = 'bar';
	 * 
	 * // Set a property using array access
	 * $item['foo'] = 'bar';
	 * ~~~~~
	 * 
	 * #pw-group-manipulation
	 *
	 * @param string $key Name of property you want to set
	 * @param mixed $value Value of property
	 * @return $this
	 * @see WireData::setQuietly(), WireData::get()
	 *
	 */
	public function set($key, $value) {
		if($key === 'data') {
			if(!is_array($value)) $value = (array) $value;
			return $this->setArray($value); 
		}
		$v = isset($this->data[$key]) ? $this->data[$key] : null;
		if(!$this->isEqual($key, $v, $value)) $this->trackChange($key, $v, $value); 
		$this->data[$key] = $value; 
		return $this; 
	}

	/**
	 * Provides direct reference access to set values in the $data array
	 * 
	 * @param string $key
	 * @param mixed $value
	 * return $this
	 *
	 */
	public function __set($key, $value) {
		$this->set($key, $value); 
	}

 

Share this post


Link to post
Share on other sites

$this->mymodule is not $mymodule

  • Thanks 1

Share this post


Link to post
Share on other sites
5 hours ago, Soma said:

$this->mymodule is not $mymodule

Thanks, fixed! I wrote the code quickly from memory... But this is not related to my question/problem.

I'd like to learn what set() exactly does in a module. And how to change a module property from outside the module. I'm probably looking in the wrong places, but I couldn't find detailed information what module properties are and how to handle them. I had a look at various other modules, but most of them are using their different ways.

Especially the difference between:

protected $foo = 'bar'; // defined a class property directly in class header

and

$this-set('foo', 'bar'); //  In class constructor

is not clear (for me).

Share this post


Link to post
Share on other sites

Well set() sets a property to the data property which is PW specific. I think it's mostly used to make it distinct data for a module or object and set data to a WireData module which is used to save data to DB like module/class configurations, change tracking etc.

Defining a property directly in the class body makes it just a normal class property which PW doesn't threat special and it won't be in the data array. 

 

protected $foo = "bar"; // standard class property

- and then set foo using set()

$this->set("foo", "baz");  // set data array only

$this->foo = "bar2"; // set the class property

- You'll end with two "foo" which are not the same

$this->foo // bar2

$this->get("foo") // baz (from data array)

 

But If you don't set a class property and only use data array or set(key, value) to define a PW data property

$this->set("foo" , "bar");

$this->foo = "bar2";

is the same "foo"

 

Just look a the WireData class

  • Like 3

Share this post


Link to post
Share on other sites

So back to your first post and what is the problem? It's hard to say as it depends what you want to do.

I think it's once you

$mymodule = $this->wire('modules')->get('MyModule');

The module is loaded and init()'d , unless it's a autoload module. So the fullname is already set.

So you want to set the fullname after you set the names or when you call execute. Or have a getter method for fullname. There's lots of ways...

<?php namespace ProcessWire;

class MyModule extends Process {

    public static function getModuleInfo() {
        return array(
            'title' => "My Module",
            'version' => 1,
        );
    }
  
    public function __construct() {
        $this->set('firstName', 'Han');
        $this->set('lastName', 'Solo');
    }

    public function init() {
    }
    
  	public function getFullName() {
    	return $this->firstName . ' ' . $this->lastName;
  	}
  
    public function ___execute() {
        return $this->getFullName();
    }
}

 

  • Like 1

Share this post


Link to post
Share on other sites

Sorry guys, problem fixed and my understanding of WireData set() and get() methods is kinda stable now! 🙂

Setting the module property values from outside the module did in fact work as expected from the beginning (the correct values are represented in module data array). I simply updated the properties in the wrong place (after the Ajax part).

To leave a reference for others I have to add/correct a few things I was told here (without stepping on anyone's toes):

On 1/24/2019 at 8:38 PM, horst said:

The properties are set as protected.

I'm not sure that this is correct (at least it doesn't matter). I simply can update properties from outside a module without extending the module class. I only need to instantiate the module.

$mymodule = $this->wire('modules')->get('MyModule');
$mymodule->foo = 'bar'; // This correctly updates the property "foo" in MyModule!

// Looking at the WireData class
$mymodule->foo = 'bar';
// gets internally translated to
$this->set('foo', 'bar');

So, If you like to have modules with simple configurable properties it seems to be the best to use the set() and get() methods of WireData instead of creating your own getter and setter methods.

Please correct me if I'm wrong.

And thanks all for your help!

  • Like 1

Share this post


Link to post
Share on other sites
6 hours ago, Gadgetto said:

Please correct me if I'm wrong.

No that's correct! 🙂

  • Like 1

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.

×
×
  • Create New...