Announcement

Collapse
No announcement yet.

Tips for adapting previous custom code to work with Espo 6.0+

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Tips for adapting previous custom code to work with Espo 6.0+

    I will be posting here some tips for re-factoring existing custom code or custom setups to work properly with Espo 6.0 + , as we work on updating our own application, it would be very helpful if others can also share here their tips to make the transition a little less painful, since this was a major upgrade and seems to trigger a lot of errors and warnings.

    Hooks:

    The previous "Espo\Core\Hooks\Base" class is deprecated now, so while it might still work for the time being, it is best to consider it obsolete and refactor all hooks that were extended from it, use the table below as an informal guide to replace former "base" hook class methods:

    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
        // some code
        $this -> getEntityManager() ->
        // more code
    }
    NOW:
    Code:
    use Espo\Core\ORM\EntityManager;
    class MyCustomHook {
        protected $entityManager;
    
        public function _construct(EntityManager $entityManager) {
            $this -> entityManager = $entityManager;
        }
    
        // some code
        $this -> entityManager ->
        // more code
    }

    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
        // some code
        $this -> getConfig() ->
        // more code
    }
    NOW:
    Code:
    use Espo\Core\Utils\Config;
    
    class MyCustomHook {
        protected $config;
    
        public function __construct(Config $config) {
            $this -> config = $config;
    
        // some code
        $this -> config ->
        // more code

    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
    
        // some code
        $this -> getMetadata() ->
        // more code
    }
    NOW:
    Code:
    use Espo\Core\Utils\Metadata;
    
    class MyCustomHook {
        protected $metadata;
    
        public function __construct(Metadata $metadata) {
            $this -> metadata = $metadata;
        }
    
        // some code
        $this -> metadata ->
        // more code
    }
    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
    
    // custom hook functions
    ...
    $this -> getContainer() ->
    ...
    }
    NOW:
    Code:
    use Espo\Core\Container;
    
    class MyCustomHook {
    protected $container;
    
    public function __construct(Container $container) {
    $this -> container = $container;
    }
    
    // custom hook functions
    ....
    $this > container ->
    ....
    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
    
    // custom hook functions
    ...
    [COLOR=#000000]$this -> getServiceFactory() ->[/COLOR]
    ...
    }
    NOW:
    Code:
    use Espo\Core\ServiceFactory;
    
    class MyCustomHook {
    protected $serviceFactory;
    
    public function __construct(ServiceFactory $serviceFactory) {
    $this -> serviceFactory = $serviceFactory;
    }
    
    // custom hook functions
    ....
    [COLOR=#000000]$this -> serviceFactory ->[/COLOR]
    ....
    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
    
    // custom hook functions
    ...
    [COLOR=#000000]$this -> getUser() -> [/COLOR]
    }
    NOW:
    Code:
    use Espo\Entities\User;
    
    class MyCustomHook {
    protected $user;
    
    public function __construct(User $user) {
    $this -> user = $user;
    }
    
    // custom hook functions
    ....
    [COLOR=#000000]$this -> user -> [/COLOR]
    ....
    BEFORE:
    Code:
    class MyCustomHook extends \Espo\Core\Hooks\Base {
    
    // custom hook functions
    ...
    [COLOR=#000000]$this -> getContainer() -> get('mailSender');[/COLOR]
    ...
    }
    NOW:
    Code:
    use Espo\Core\Di;
    
    class MyCustomHook implements Di\EmailSenderAware {
    
    use Di\EmailSenderSetter;
    
    // custom hook functions
    ....
    [COLOR=#000000]$mailSender = $this -> emailSender -> create();[/COLOR]
    ....

    Entry Points:

    Entry points can only be invoked now using GET requests, if your code has entry point calls using POST requests (like we had because in some cases I didn't want to display the payload parameters in the browser's url) you could replace the entry point call with a standard Ajax - Controller - Service -Controller - Ajax implementation as follows:

    BEFORE: (view code)
    Code:
    ......
    var url = '?entryPoint=chatNotification';
    var options = {
        notificationType: 'eMail',
        authorId: authorId,
        subject: subject,
        body: body,
        portalBody: portalBody,
        distributionList: distributionList,
        portalDistributionList: portalDistributionList
    };
    var payload = JSON.stringify(options);
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST", url);
    xmlhttp.setRequestHeader("Content-type", "application/json");
    var self = this;
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState === XMLHttpRequest.DONE) {
            if (xmlhttp.status === 200) {
                console.log("Email Notifications sent to: ",xmlhttp.responseText);
            } else if (xmlhttp.status === 400) {
                alert('There was an error 400');
            } else {
            alert('something else other than 200 was returned');
            }
        }
    };
    xmlhttp.send(payload);
    BEFORE: (entry point class code)
    PHP Code:
    ......
    class 
    ChatNotification extends \Espo\Core\EntryPoints\Base
    {
        public function 
    run()
        {
            
    //convert the POST JSON input received into a PHP associative array
            
    $requestPayload file_get_contents("php://input");
            
    $payload json_decode($requestPayloadtrue);
            
    $notificationType $payload["notificationType"];
            if(
    $notificationType === "eMail") {
                
    $this -> getServiceFactory() -> create('ChatNotification') -> emailNotification($payload);
            } elseif(
    $notificationType === "sms") {
                
    $this -> getServiceFactory() -> create('ChatNotification') -> smsNotification($payload);
            }
        }



    NOW: (view class code)
    Code:
    ......
    var payload = {
        notificationType: 'eMail',
        authorId: authorId,
        subject: subject,
        body: body,
        portalBody: portalBody,
        distributionList: distributionList,
        portalDistributionList: portalDistributionList
    };
    // Make the request to the back-end: Ajax Call > PHP Controller > PHP Service > PHP Controller > Ajax Response
    this.ajaxPostRequest('ChatPostNotification/action/sendChatActivityNotification', payload).then(
        function (responseData) {
            var serverResponse = '';
            if(typeof(responseData === "object")) {
                serverResponse = JSON.parse(responseData);
            } else {
                serverResponse = responseData;
            }
            console.log("Email Notifications sent to: ",serverResponse);
        }.bind(this)).fail(
        function (xhr) {
            xhr.errorIsHandled = true;
        }
    );
    NOW: (back-end controller class code)
    PHP Code:
    public function postActionSendChatActivityNotification($params$data) {
        
    // build the payload to send to the Service class
        
    $payload = (object)[];
        
    $payload- > notificationType $data -> notificationType;
        
    $payload -> authorId $data -> authorId;
        
    $payload -> subject $data -> subject;
        
    $payload -> body $data -> body;
        
    $payload -> portalBody $data ->portalBody;
        
    $payload -> distributionList $data -> distributionList;
        
    $payload -> portalDistributionList $data -> portalDistributionList;
        
    // invoke the appropriate service function depending on the type of notification requested
        
    if($payload notificationType === "eMail") {
            
    $response $this -> getServiceFactory() -> create('ChatNotification') -> emailNotification($payload);
        } elseif(
    $payload -> notificationType === "sms") {
            
    $response $this -> getServiceFactory() -> ('ChatNotification') -> smsNotification($payload);
        }
        return 
    $response;

    NOW: (back-end service class code)
    PHP Code:
    public function emailNotification($payload) {
        
    // Code to prepare and send notifications via eMail and store the list of email addresses to which the notifications were sent
        
    ......
        
    // return the list of email addresses to the controller
        
    echo json_encode($sentList);


    Service Classes:

    The base class Espo\Services\Services.php was refactored, custom service class that extend from it will need the following modifications:
    BEFORE NOW
    $this -> getSelectAttributeList($params); $this -> getSelectManager() -> getSelectAttributeList($params);
    Last edited by telecastg; 02-09-2021, 05:44 AM.

  • #2
    Hello telecastg

    i will back with your post ... let me time to understand first
    (pfff for markup issue.. )
    Thanks for all

    Comment


    • #3
      Thanks for the info!!!

      Comment


      • #4
        Hi telecastg,
        I want to ask you if you can help me, before 6.x I developed a utils class shared with all hooks.
        Now I have the problem that $this.container is null and I can't understand why.

        Is there a way to develop a custom re-usable class? I can't understand where I have to place my class to auto-load dependencies, or what to extend.

        Thanks
        Last edited by nicolar; 02-01-2021, 03:44 PM.

        Comment


        • #5
          Hi nicolar, I'm sorry but I don't have enough knowledge to help you with dependencies, my back-end skills beyond actual PHP coding are quite limited.

          Perhaps some other participant can help you or if not, I would suggest to check the core hooks code https://github.com/espocrm/espocrm/t...ion/Espo/Hooks and see if you can get a hint on how shared util classes are handled now.

          Comment


          • item
            item commented
            Editing a comment
            Hello telecastg,

            no skill too ... really nothing my custom work with v6.
            i use many custom vendor/library... with v6, it's ignored.
            i have too many utils/class .... so i imagine not work
            Maybe, Yuri need to give some sample.

            Regards

          • nicolar
            nicolar commented
            Editing a comment
            Thank you, I'm watching the code to understand how dependencies are loaded and where I need to put my classes.
            I think there are some global metadata where I can declare file/namespaces to load.

        • #6
          The only one way I found to initialize the container correctly is to pass the container variable in the constructor of my custom class.
          In the specific case of hooks, you need to init the custom object in the beforeSave method to be sure you have the container variable.

          Comment


          • #7
            I would like to learn more about dependencies. Could you post a snippet of your code to see how they are handled for hooks ? Thanks in advance.

            Comment


            • item
              item commented
              Editing a comment
              I like because i don't understand why we need to do so now.

              why
              $em = $this->getEntityManager();
              now need to add in function _construct...

              what's is the goal ?

            • tothewine
              tothewine commented
              Editing a comment
              the constructor gets passed an entity manager now. it's a different form of dependency injection I guess
          Working...
          X