Jump to content

How do I extend $user?


Hani
 Share

Recommended Posts

I'd like to add some functions to $user. For example I'd like to be able to do the following:

$user->sendThankYou();

I tried creating the following module:

class UserExtended extends User implements Module{

	public static function getModuleInfo() {
			return array(
					'title' => 'Extended User Module',
					'version' => 100,
					'summary' => 'Extends Processwire\'s Core User Module',
					'singular' => true,
					'autoload' => true
					);
	}

	public function sendThankYou() {
			// Do something
	}
}

But I get the following error:

Class UserExtended contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Module::init) (line 18 of /home/hani/public_html/site/modules/UserExtended.module)

At that point, I thought, "Of course. "User" isn't a really a module. It's not in the Modules list. It extends Page."

I'd really appreciate any direction on how to do this.

Link to comment
Share on other sites

/site/modules/UserExtension.module

<?php
class UserExtension extends WireData implements Module {
public static function getModuleInfo() {
 return array(
  'title' => 'User Extension',
  'version' => 101,
  'summary' => 'Extends $user object.',
  'singular' => true,
  'autoload' => true,
  );
}
public function init() {
 $this->addHook('User::sendThankYou', $this, 'sendThankYou');
}
public function sendThankYou($event) {
 $user = $event->data['object'];
 $event->return = "Thank you, $user->name";
}
}

Usage is dead simple:

<?php echo $user->sendThankYou() ?>
  • Like 3
  • Thanks 1
Link to comment
Share on other sites

I suggest not extending the User class, and instead plugin to it with a module

class UserExtended extends WireData implements Module {
   public static function getModuleInfo() { /* return your array here */ }
   public function init() { $this->addHook('User::sendThankYou', $this, 'sendThankYou'); 
   public function sendThankYou($event) { /* do something */ }
}

The above would add a sendThankYou method to all User instances.

Your sendThankYou method can gain access to the $user the method was called on like this:

$user = $event->object; 

Your sendThankYou method can also have one or more arguments if you want it to:

$arg = $event->arguments[0]; 

If you want your sendThankYou method to return a value, do it like this:

$event->return = 'value you want to return';

Looks like Antti beat me to it. :)

  • Like 5
Link to comment
Share on other sites

  • 5 years later...

Hello! I started to extend my user using a new class.

All works fine, I am a little unfamiliar with the $event system, (Why can't we just have usual return values? This would make exiting a function at the right time a lot easier. Also plain function arguments would let me allow to better understand how to pass the $event to other functions) but it works.

Now I want to re-use existing functions in other functions of the same class and it just does not work. The other functions in the same class are not callable and have - you guessed it - no return values.

public function init() {
	$this->addHook('User::getMessages', $this, 'getMessages');
	$this->addHook('User::getMessagesBySender', $this, 'getMessagesBySender');
}

public function getMessages($event)
{
  $user = $event->object;
  $event->return = $this->pages->find('template=message, receiver='.$user);
}

public function getMessagesBySender($event)
{
  $user = $event->object;
  $sender = $event->arguments[0];
  $event->return = $this->getMessages($event)->find('sender='.$sender);
}

What can I do to get the return values of other hooked methods?

If I var_dump($this->getMessages($event)); I get NULL.

How should I go about this? Are there maybe better methods of extending the user class in 2017?

As you can see, getMessages is both a function I want to re-use, but also a hooked function.

Thank you in advance!

Link to comment
Share on other sites

hi @Martin Muzatko

i would recommend you read the docs about hooks carefully: https://processwire.com/api/hooks (especially here: https://processwire.com/api/hooks/#add_new_method )

to your problem:

try this:

public function getMessagesBySender($event)
{
  $user = $event->object;
  $sender = $event->arguments[0];
  $event->return = $user->getMessages()->find('sender='.$sender);
}

you could also modify your getMessages method to take an argument as selector and then use it like this:

$user->getMessages("sender=$user"); // additional tip: double quotes makes it easier to read sometimes ;)

 

regarding the "event system":

$event in the hooks is the hookevent object. you could call it whatever you want, but as it's called $event everywhere in PW it's good to stay with this naming. the object is needed by PW and it's hook system so you cannot just "return" from inside the method. or to be more clear: you can just return but then it will not change anything. thats the way how you can do early exits:

$wire->addHookAfter('Pages::added', function($myHookEvent) {
  $page = $myHookEvent->arguments(0);
  if($page->template != 'mytemplate') return; // example of early exit
  [...]
}

here i named $event differently just as a showcase.

the $event->return property holds the "value" of the hooked method. this can be different things, for example a PageArray if you hook a page find operation or a simple string if you hook a field's value. This value is only one thing that is needed by processwire to fully execute the request. For example there is also the $event->replace property that would tell PW to completely replace the hooked method if it was set to TRUE - so pw would not call the original method after the hook was executed (this only works on before-hooks, of course). https://processwire.com/api/hooks/#replace_method

hope this makes sense for you.

 

i would also recommend using tracy debugger - it's a lot more powerful and easier to read than var_dump ;)

  • Like 5
Link to comment
Share on other sites

  • 6 years later...

Though this is very helpful, I am still not sure how this works.

I can do stuff like this:

On 9/11/2017 at 8:48 AM, Martin Muzatko said:
public function init() {
	$this->addHook('User::getMessages', $this, 'getMessages');
	$this->addHook('User::getMessagesBySender', $this, 'getMessagesBySender');
}

public function getMessages($event)
{
  $user = $event->object;
  $event->return = $this->pages->find('template=message, receiver='.$user);
}

public function getMessagesBySender($event)
{
  $user = $event->object;
  $sender = $event->arguments[0];
  $event->return = $this->getMessages($event)->find('sender='.$sender);
}

i.e. access the custom public methods of the user object from outside the class. I have also of course no problem accessing private classes within the here discussed user extension class.

However, how does the code change when I define and use a public method from inside and from outside the class. Do I need to pass it the $event?

Link to comment
Share on other sites

@fruid Use custom page classes.

In site/classes:

<?php namespace ProcessWire;

class UserPage extends User
{
  public function getMessages(): PageArray
  {
      return wire()->pages->find('template=message, receiver=' . $this->id);
  }
}

Be sure to enable it in site/config.php

$config->usePageClasses = true;
  • Like 4
Link to comment
Share on other sites

19 hours ago, da² said:

Use custom page classes.

I thought extending the user class was not possible since it's a special case and I should use the module approach instead. Has that change in the meantime?

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...