Jump to content

module inheritance


Matzn
 Share

Recommended Posts

Hi,

how I can access parent modules config fields? It is also not possible to use the parent methods include this fields.

Example: I need field "api_user" in Child class.

class Parent extends WireData implements Module
{
	public function getApiUser()
	{
		return $this->api_user;
	}

}
class ParentConfig extends ModuleConfig
{

	public function getInputfields()
	{
		$inputfields = parent::getInputfields();

		$f = $this->modules->get('InputfieldText');
		$f->attr('name', 'api_user');
		$f->label = 'API User';
		$f->required = true;
		$inputfields->add($f);

		return $inputfields;
	}
}


class Child extends Parent implements Module
{
	//null
	$apiUser = $this->api_user;
	//null
	$apiUser = $this->getApiUser();
	
}

 

Link to comment
Share on other sites

Hi @Matzn

<?php namespace ProcessWire;

class ParentModule extends WireData implements Module, ConfigurableModule
{
	public static function getModuleInfo() {
		return [
			'title' => 'Parent Module',
			'version' => 1,
		];
	}

	const defaultValue = '12345';

	public function __construct() {
		$this->set('api_user', self::defaultValue); // set default value in construct
	}

	public function getApiUser()
	{
		return $this->api_user;
	}

	public static function getModuleConfigInputfields(array $data) {
		if(!isset($data['api_user'])) $data['api_user'] = self::defaultValue;

		$form = new InputfieldWrapper();
		$f = wire('modules')->get('InputfieldText');
		$f->name = 'api_user';
		$f->label = 'API USER';
		$f->value = $data['api_user'];
		$form->add($f);

		return $form;
	}
}


<?php namespace ProcessWire;

class ChildModule extends ParentModule
{

	public static function getModuleInfo() {
		return [
			'title' => 'ChildModule',
			'version' => 1
		];
	}

	public function __construct() {
		parent::__construct();
		bd($this->getApiUser());
		bd($this->wire()->modules->getModuleConfigData('ParentModule'));
	}
}

Take a look at the construct method of ChildModule. 

Without calling parent::__construct you will not be able to get what you want. 

Also you can use $this->wire()->modules->getModuleConfigData('ParentModule') to get config data of module. 

  • Like 4
Link to comment
Share on other sites

Hey @Zeka,

thanks. If i understood, now get the const "1234" from parent class. But wat i need is the admin field from modul (screenshot). Some magic with ParentConfig class must do. But what? 

 

screenshot-shopuniver.com-2022.05.09-13_29_06.jpg

Link to comment
Share on other sites

@Matzn Are you wanting a parent/child class relationship, or are parent and child modules separate modules where you want the child module to pull a value from the parent module? If you just want to pull a configuration value from "ParentModule" to use somewhere in "ChildModule" (or anywhere else) then you don't need to have a parent/child relationship between the modules and can instead just pull the value you need: 

$api_user = $modules->getConfig('ParentModule', 'api_user'); 

On the other hand, if you really do want a parent/child relationship between the module classes, and they will be two separate installed modules, then it is possible. Many Inputfield and Fieldtype modules use this approach. For instance, InputfieldCKEditor extends InputfieldTextarea, and InputfieldTextarea extends InputfieldText. The reason is that they all have similar configuration needs, as they are all inputs for text. InputfieldCKEditor builds upon the configuration of InputfieldTextarea and InputfieldTextarea builds upon the configuration of InputfieldText. 

When it's possible that someone might attempt to install the ChildModule without having first installed the ParentModule then it's recommended to keep your module-info in a separate php (or json) file like ModuleName.info.php, at least for the modules that require another (i.e. ChildModule requires ParentModule). This ensures that if ChildModule is on the file system, but ParentModule isn't, PW can identify the requirements for the ChildModule without having to load its class file. (If it loaded its class file, then it would result in an error since ChildModule extends ParentModule, and ParentModule isn't present, so that's why we want to avoid that). 

Let's say we've got this ParentModule:

// ParentModule.module.php
class ParentModule extends WireData implements Module, ConfigurableModule {
  public function __construct() {
    $this->set('api_user', ''); // establish default value
    parent::__construct();
  }
  public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
    $f = $this->wire()->modules->get('InputfieldText');
    $f->attr('name', 'api_user');
    $f->label = 'API User';
    $f->required = true;
    $f->val($this->get('api_user')); 
    $inputfields->add($f);
  }
}

And it also has this ParentModule.info.php file to define module info. Since ParentModule doesn't extend another module, it could also be in a static getModuleInfo() method if you preferred it, but we'll stick to the file for this example:

// ParentModule.info.php
<?php namespace ProcessWire;
$info = array(
  'title' => 'Parent module',
  'version' => 1,
  'summary' => 'Parent module description',
);

Here's a child module that extends ParentModule and also adds an "api_key" to the configuration:

// ChildModule.module.php
class ChildModule extends ParentModule implements Module, ConfigurableModule {
  public function __construct() {
    $this->set('api_key', ''); // default value
    parent::__construct();
  }
  public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
    parent::getModuleConfigInputfields($inputfields);
    $f = $this->wire()->modules->get('InputfieldText');
    $f->attr('name', 'api_key');
    $f->label = 'API Key';
    $f->required = true;
    $f->val($this->get('api_key')); 
    $inputfields->add($f);
  }
  public function test() {
    $this->message("API user is $this->api_user and key is $this->api_key"); 
  }
}

…and the ChildModule's info file is below. Note the "requires" that says it requires ParentModule:

// ChildModule.info.php
<?php namespace ProcessWire;
$info = array(
  'title' => 'Child module',
  'version' => 1,
  'summary' => 'Child module description',
  'requires' => 'ParentModule',
);

Zeka's example above is also a good one, but just adding my example as another variation. 

  • Like 3
Link to comment
Share on other sites

@Zeka @ryan

Hui, now i am confused... Yes, I want a parent/child class relationship like php class extend.

I'm writing modules as on this page discribed: ProcessWire 2.5.5 – New module configuration

The modul file(excerpt)

//file: parent.modul
class Parent extends WireData implements Module
{

	public static function getModuleInfo()
	{
		return array(

			'title' => 'Network Adcell',
			'version' => 1,
			'summary' => 'Loading Data from Affiliate Network.',
			'singular' => true,
			'autoload' => false,

		);
	}
	public function getToken()
	{
		$data = $this->_request(
			'user',
			'getToken',
			[
				'userName' => $this->api_user,
				'password' => $this->api_key,
			]
		);

		return $data->data->token;
	}

	public function test()
	{
		$data = [
				'userName' => $this->api_user,
				'password' => $this->api_key,
			];

		return $data;
	}

}

The parent modul config file:

//file: ParentConfig.php

class ParentConfig extends ModuleConfig
{

	public function getInputfields()
	{
		$inputfields = parent::getInputfields();

		$f = $this->modules->get('InputfieldText');
		$f->attr('name', 'api_user');
		$f->label = 'API User';
		$f->required = true;
		$inputfields->add($f);

		$f = $this->modules->get('InputfieldText');
		$f->attr('name', 'api_key');
		$f->label = 'API Key';
		$f->notes = 'Der API Key muss im Adcell Backend erstellt werden.';
		$f->required = true;
		$inputfields->add($f);

		return $inputfields;
	}
}

 

The parent modul is an old modul that i can't/wan't thouch. So i think i extend the parent class with new methods.

The child modul (excerpt)

class Child extends Parent implements Module
{
	public static function getModuleInfo()
	{
		return array(
			'title' => 'Import Adcell programs',
			'version' => 1,
			'summary' => 'Import new joined programs and offers.',
			'singular' => true,
			'autoload' => false,
			'requires' => 'Parent modul'

		);
	}

	public function addProgram()
	{

		/* this will get:
        * array(2) {
        *  ["userName"]=>NULL
        *  ["password"]=>NULL
        * }
		*/
		$token = $this->test();

		//some further code

	}
	
}

 

In child modul if call "$this->test();" get null, because (i think) the ParentConfig will not extend/call what ever. Also is $this->api_user=null and $this->api_key=null. A simple solution is to add a constant/property to parent modul, but i wan't touch this file.

I hope we don't talk past each other?

Link to comment
Share on other sites

Maybe I also have mistakes in thinking and a processwire class can't extend like php. Is that so?

I get the parent methodes and properties like:

class Child extends Parent implements Module
{
	
	public function myMethode()
	{
		$myParentModul = wire('modules')->get('Parent');
		$token = $myParentModul->test();
	}
}

 

Link to comment
Share on other sites

@Matzn ProcessWire classes are just PHP classes, so there would be no difference. Note that I don't think you can have a class named "Parent" as I think that might be a reserved word in PHP. I wouldn't bother trying to do an external configuration class. Start with an internal getModuleConfigInputfields() method, and when all works, then consider moving it to an external one if needed. Make sure your module implements the ConfigurableModule interface, indicated at the top of the class. I also think maybe you shouldn't try to extend your Parent module/class and instead should just write another module that pulls from the Parent module what it needs. Module inheritance is useful in many situations but it's not clear to met that this is one of those situations, though I could be wrong. 

Quote

I get the parent methodes and properties like:

This is fine the way you are doing it, but note that your class "Child" need not extend "Parent" given the approach you are using. Your "Child" module could just extend WireData instead, and the code in your myMethode() would do exactly the same thing. 

  • Like 2
Link to comment
Share on other sites

@ryan No, i m using other class names. My IDE helps me for wrong syntax.

It was not possible for me to get the properties api_user and api_key in a child class. No matter which variant I tried (intern or extern modul config im testing). The values were not available in the children's class. Why, i don't know, maybe i do some ? 

Link to comment
Share on other sites

@Zeka @ryan

I tested again and found that the "ModuleConfigInputfields" are not inherited. This means that the parent methods not can be call from child class, if "ModuleConfigInputfields" are used in parent class. To get "ModuleConfigInputfields" for inherited modul/class must get first the module like: wire()->modules->get('modulname);

Here's the proof:

<?php
//template file
namespace ProcessWire;

$m1= $modules->get("TestModule");
print_r($m1->getField());

$m2 = $modules->get("TestModuleChild");
print_r($m2->getField());
//output
Array
(
    [field 1] => parent module property 1
    [field 2] => Parent Module field 2
)
Array
(
    [field 1] => parent module property 1
    [field 2] => default field 2
)

 

parent_module.png

child_module.png

TestModule.module TestModuleChild.module

Link to comment
Share on other sites

@Matzn Your TestModuleChild.module is overriding the getModuleConfigInputfields() of the TestModule.module and replacing it. In PHP, if you want the parent method to run, then you need to either omit the  method completely in your child class, or call the method parent::method() so that it will run (like what you are doing with your __construct method). What you are doing currently is replacing the method with one that has no code in it.  What I think you are wanting is this in your TestModuleChild.module: 

public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
  parent::getModuleConfigInputfields($inputfields);
}

After doing the above, now it will inherit the code of the parent class. 

And after calling that parent::method() maybe you want to add another configuration setting to it, i.e. 

public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
  parent::getModuleConfigInputfields($inputfields);
  $f = $this->wire()->modules->get('InputfieldText');
  $f->attr('name', 'test_field');
  $f->label = 'Test field';
  $f->val($this->test_field); 
  $inputfields->add($f);
}

But if you don't want to add anything new to the getModuleConfigInputfields() then just leave the method out of your TestModuleChild.module completely, and it will inherit automatically. 

Also, module configuration data should be defined with runtime calls, so I recommend you remove this: 

public $field_1 = "parent module property 1";

and replace it with this in your __construct():

$this->set('field_1', 'parent module property 1'); 

I see you are using static getModuleInfo() methods in your classes. In cases where you've got one module extending another, unless the child module literally does an include() at the top of the parent module's file, then you should define your child module's info in a ModuleName.info.php file like I described in my previous message (including the 'requires' statement), and omit the getModuleInfo() static method. Otherwise, if PHP/PW happens to include() the child module before the parent module, you'll get a fatal class error. By using a separate ModuleName.info.php, PW can learn about the module without having to load the class file, and it can see from your 'requires' statement that it needs to load the parent module before your child module, ensuring no fatal errors. If you can't do this, or don't want to, then make sure your child module does an include() or require() of the parent module's file, somewhere before your "class TestModuleChild ...". 

  • Like 3
Link to comment
Share on other sites

Thank you for your description. You're right if some syntax isn't 100% correct. But what I mean is the thing is that I can't call a parent function from a child function because the parent field value doesn't inherit.

$m2 = $modules->get("TestModuleChild");
print_r($m2->getField());//function will not overwrite in child class

The function result is (note field 2)

    [field 1] => parent module property 1
    [field 2] => default field 2

I would have expected (note field 2), because i have input it on parent module (screenshot Parent Module)

[field 1] => parent module property 1
[field 2] => Parent Module field 2

 

So I'm wondering if I can extend processwire class like a php class at all. At least not as a module extension.

Link to comment
Share on other sites

@Matzn that not.how obj inheritennce work {
.clsses inherit code 
  not runs time valus }

 

class A{public $foo;}
class B extends A{}
$a=new A();
$a->foo='Foo';
$b=new B();
echo $a->foo; //  'Foo'
echo $b->foo; // emptys.. nothing

 

modules work.same {
  .mabe.u want this  in 
  you TestModuleChild ? } >>>

public function __construct(){
  $this->setArray($this->modules->getConfig('TestModule'));
}

  • Like 4
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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...