Magento2 - how do I call a function in another class? - magento2

I'm creating an observer and I'm trying to connect it to saving a new product. If the product is new I'm trying to run a code, if not - skip.
I made an observer before save in creating a new product and after that I want to run the same code as shared catalog -> shared catalogs -> default -> set pricing and structure -> and just save without changing anything.
This is my code:
NewProductReindexer/etc/adminhtml/events.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_product_save_before">
<observer name="Company_module_event_after_observer" instance="Company\NewProductReindexer\Observer\NewProductObserver"/>
</event>
Observer/NewProductObserver.php
class NewProductObserver implements ObserverInterface
{
public function __construct()
{
}
public function execute(Observer $observer)
{
$isProductNew = $product->isObjectNew();
if($isProductNew == true){
//new product
}else{
relax();
}
}
}

The two ways to call you a block class method in the custom module class
using constructor dependency like that
public function __construct(Namespace\ModuleName\Helper\Data $helper)
{
$this->helper = $helper;
}
public function MyFunction()
{ $this->helper->HelperDemo();
}
using method dependency public function execute(Namespace\ModuleName\Helper\Data $helper) { $helper->HelperDemo(); }
For more detail visit the below link https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html

Related

Suitecrm custom template for custom module

I built a custom module using the module builder.
I want to modify templates of my module (edit, detail, subpanel).
How can I tell Suitecrm to use an other template?
Thanks
(Suitecrm 7.7)
it's work for me.
<?php
require_once('include/MVC/View/SugarView.php');
class AccountsViewEdit extends SugarView {
private $smarty;
public function __construct() {
}
public function display() {
$this->smarty = new Sugar_Smarty();
$data = ['a'=> 'a', 'b'=>'b'];
$this->smarty->assign($data);
$this->smarty->display('path/custom/template.tpl');
}
}
You need to create a SugarView in the module and then override the display() method to return the path to your custom template. The convention is keep your templates in a 'tpl' folder in the module.
For example if you look at the 'modules/Accounts/views/view.edit.php' you would just need to add
class AccountsViewEdit extends ViewEdit
{
public function __construct()
{
parent::__construct();
$this->useForSubpanel = true;
$this->useModuleQuickCreateTemplate = true;
}
public function display() {
parent::display(); // TODO: Change the autogenerated stub
return $this->ss->fetch('path/to/your/smarty/template.tpl');
}
}
it's almost the same for the subpanels except the location is in the Dashlets folder. Take a look at modules/Accounts/Dashlets/MyAccountsDashlet/MyAccountsDashlet.php for example.

Can't get mysqli identifier from another class

I have two classes: one for sql and one for authentication. These are separate files that only "come together" by an autoloader in another file (index.php). The sql class looks like this:
class aF_sql {
public static $open;
public static $char;
public static $close;
public static function open() {
global $config;
self::$open = mysqli_connect($config['aF_sql_host'], $config['aF_sql_user'], $config['aF_sql_pass'], $config['aF_sql_data']);
if(!self::$open) {
die('<b>ERROR (/core/sql/1): Could not initialize database connection</b>');
}
self::$char = mysqli_set_charset(self::$open, $config['aF_sql_char']);
if(!self::$char) {
die('<br>ERROR (/core/sql/2): Could not set database charset</br>');
}
return TRUE;
}
public static function close() {
self::$close = mysqli_close(self::$open);
if(!self::$close) {
die('<b>ERROR (/core/sql/3): Could not close database connection');
}
return TRUE;
}
}
Everything works fine until here. I open the sql connection by using the class::open(); method and I close it by using class::close();. Then, I have a file that should help me check some login details (the auth class). For now, all I want is an echo of the submitted username. Here is the content:
class aF_auth {
public static function login() {
echo mysqli_real_escape_string(aF_sql::open, $_POST['username']);
}
}
Thisis what the error says: Warning: mysqli_real_escape_string() expects parameter 1 to be mysqli, null given... What should I use as an identifier to be able to perform queries? As you know, mysqli requires the connection identifier.
Thanks a million
Ok, I figured it out! Instead of returning TRUE in the class, I return self::$open. This way, I can use aF_sql::$open everywhere in the software and it refers to the connection identifier. With other words, it didn't work because I was not returning the variable in the class...
class aF_sql {
public static $open;
public static $char;
public static $close;
public static function open() {
global $config;
self::$open = mysqli_connect($config['aF_sql_host'], $config['aF_sql_user'], $config['aF_sql_pass'], $config['aF_sql_data']);
if(!self::$open) {
die('<b>ERROR (/core/sql/1): Could not initialize database connection</b>');
}
self::$char = mysqli_set_charset(self::$open, $config['aF_sql_char']);
if(!self::$char) {
die('<br>ERROR (/core/sql/2): Could not set database charset</br>');
}
return self::$open;
}
public static function close() {
self::$close = mysqli_close(self::$open);
if(!self::$close) {
die('<b>ERROR (/core/sql/3): Could not close database connection');
}
return TRUE;
}
mysqli_real_escape_string(aF_sql::$open, $_POST['username']);
That's it...

log4net: How to distinguish between different forms on the same UI thread?

is there a way (NDC, Properties, ...?) to have a name/id per form that is included in all log4net messages, so I can distinguish between the forms in all log messages?
I have many service methods etc. that are used in all my forms, and I'd like to see e.g. that a service was called as a result of user input in what form (think multiple nonmodal similar forms (same class), running in the same UI thread, containing a button, and in the button's Click-Event, a service method is called. Inside the service method, there are logging calls. In the log messages, I'd like to have a property containing the information of in exactly which form instance the button was clicked in).
I don't want to modify ALL logging calls. The examples in the web for log contexts / NDC all only talk about multiple clients / asp.net requests / etc., not multiple forms in 1 thread.
Thanks,
Tim
To do this, set the properties in the form's Activated event to what you want to log:
private void Form1_Activated(object sender, System.EventArgs e)
{
// for example
log4net.GlobalContext.Properties["Name"] = this.GetType().Name;
log4net.GlobalContext.Properties["Id"] = this.Id;
}
The in your logging configuration, you can reference the properties in the PatternLayout for each appender:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Name} : %property{Id} : [%level]- %message%newline" />
</layout>
Edit: to preserve multiple values, use a stack, as in this unit test which outputs:
Now in TestClass1 Now in TestClass2
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using NUnit.Framework;
namespace log4net.Tests
{
[TestFixture] // A NUnit test
public class log4net_Stacks
{
[SetUp]
public void Setup()
{
ConsoleAppender ca = new ConsoleAppender
{
Layout = new PatternLayout("%property{demo}"),
Threshold = Level.All
};
ca.ActivateOptions();
BasicConfigurator.Configure(ca);
}
[Test]
public void Stacks_Demo()
{
new TestClass1().Method1();
LogManager.GetLogger("logger").Debug("");
ThreadContext.Stacks["demo"].Clear();
}
private abstract class BaseTestClass
{
protected static void AddToStack(string message)
{
ThreadContext.Stacks["demo"].Push(message);
}
}
private class TestClass1 : BaseTestClass
{
public void Method1()
{
AddToStack("Now in " + GetType().Name);
var tc2 = new TestClass2();
tc2.Method2();
}
}
private class TestClass2 : BaseTestClass
{
public void Method2()
{
AddToStack("Now in " + GetType().Name);
}
}
}
}

Get route params in RESTful controller

In my RestController which extends AbstractRestfulController, I can get the route params in the implemented functions such as...
public function create($data)
{
$entity = $this->params()->fromRoute('entity');
}
... but when I do the same in the constructor like this
public function __construct()
{
$entity = $this->params()->fromRoute('entity');
}
I get Call to a member function getParam() on a non-object.
Why is that? How can I get the route parameters in the constructor?
What I am trying to do
Since I'm trying to create a generic controller, there is a part of the restful route that is shared for all actions (resp. verbs). The entity for which the request is made. I'd like to store this in a class parameter for convenience.
Normally you'd write a method to proxy to whatever value you need, and just call that method, it's only a little more expensive to call $this->getEntity() than it is to call $this->entity, which, as far as I can tell is the stated aim
class RestController
{
protected $entity;
public function getEntity()
{
if (!$this->entity) {
$this->entity = $this->params()->fromRoute('entity');
}
return $this->entity;
}
}
If you really do want to pre-populate the entity property, the simplest method is to use an initializer, and move the code from your __constructor to init(). Have your controller implement \Zend\Stdlib\InitializableInterface
use Zend\Stdlib\InitializableInterface;
class RestController extends AbstractRestfulController implements InitializableInterface
{
protected $entity;
public function init() {
$this->entity = $this->params()->fromRoute('entity');
}
}
Add an initializer to the controller loader in your module boostrap
use Zend\Stdlib\InitializableInterface;
class Module
{
public function onBootstrap(MvcEvent $e)
$sm = $e->getApplication()->getServiceManager();
$controllers = $sm->get('ControllerLoader');
$controllers->addInitializer(function($controller, $cl) {
if ($controller instanceof InitializableInterface) {
$controller->init();
}
}, false); // false tells the loader to run this initializer after all others
}
}
That would not make any sense as the route is matched to a particular action.
You can't route to a constructor, therefore how could you get route parameters there?
If you give an idea of what you are trying to do then I could suggest a better/nicer way to do it

Does Castle-Windsor support ForwardedTypes via XML configuration

I have a class that implements multiple interfaces.
I would like to register these interfaces via XML.
All I've found is documentation for the new Fluent Interface.
Is this option supported via XML?
What would be involved in adding this feature?
[Update] This is now possible in Windsor 2.1 or newer. See the documentation for syntax here.
This feature has not been implemented in the XML interpreter as of yet.. however it is not difficult to add support for it via a facility (obviously this technique is also useful when wanting to add other features absent from the existing configuration parser).
So first off we add a facility which will detect when a handler is created for a type, and at the same time will register any of the forwarded services so they point to the existing handler:
public class HandlerForwardingFacility : AbstractFacility
{
IConversionManager conversionManager;
protected override void Init()
{
conversionManager = (IConversionManager)Kernel.GetSubSystem(SubSystemConstants.ConversionManagerKey);
Kernel.HandlerRegistered += new HandlerDelegate(Kernel_HandlerRegistered);
}
void Kernel_HandlerRegistered(IHandler handler, ref bool stateChanged)
{
if (handler is ForwardingHandler) return;
var model = handler.ComponentModel;
if (model.Configuration == null) return;
var forward = model.Configuration.Children["forward"];
if (forward == null) return;
foreach (var service in forward.Children)
{
Type forwardedType = (Type)conversionManager.PerformConversion(service, typeof (Type));
Kernel.RegisterHandlerForwarding(forwardedType, model.Name);
}
}
}
And then of course we need to make use of this in code, for this example I'm going to have a mutant duck/dog component that supports two separate services - IDuck and IDog:
public interface IDog
{
void Bark();
}
public interface IDuck
{
void Quack();
}
public class Mutant : IDog, IDuck
{
public void Bark()
{
Console.WriteLine("Bark");
}
public void Quack()
{
Console.WriteLine("Quack");
}
}
Now to actually configure the container:
<castle>
<facilities>
<facility id="facility.handlerForwarding" type="Example.Facilities.HandlerForwardingFacility, Example" />
</facilities>
<components>
<component id="mutant" service="Example.IDog, Example" type="Example.Mutant, Example">
<forward>
<service>Example.IDuck, Example</service>
</forward>
</component>
</components>
</castle>
And now we can happily execute a test like this:
WindsorContainer container = new WindsorContainer(new XmlInterpreter());
var dog = container.Resolve<IDog>();
var duck = container.Resolve<IDuck>();
Assert.AreSame(dog, duck);
Hope this helps.