How to extends a facade in laravel? - php

I'm try extends a facede in laravel 4, but I only get a next error on try calling a method.
Non-static method App\Libraries\Theme::setActive() should not be called statically
Edit
After responce of #Antonio, to change the method to static, let the power of using the keyword $ this-> inside the method.
Symfony \ Component \ Debug \ Exception \ FatalErrorException Using
$this when not in object context in $active = $this->ensureRegistered($active);
My code:
<?php namespace App\Libraries;
use Cartalyst\Themes\Facades\Theme as ThemeBag;
class Theme extends ThemeBag {
/**
* Sets the active theme.
*
* #param mixed $active
* #return Cartalyst\Themes\ThemeInterface
*/
public static function setActive($active)
{
$active = $this->ensureRegistered($active);
if ( ! isset($this->themes[$active->getSlug()]))
{
$this->register($active);
}
$this->active = $active;
include $this->getActive()->getPath() . '\\helpers\\composers.php';
}
}

Basically you'll have to extend an existing Facade:
<?php namespace AntonioRibeiro\Libraries;
class MyEventFacade extends Illuminate\Support\Facades\Event {
/**
* Sets the active theme.
*
* #param mixed $active
* #return Cartalyst\Themes\ThemeInterface
*/
public static function setActive($active)
{
/// do what you have to do
}
}
And then replace (or add it as a new one) to your app/config/app.php:
'aliases' => array(
'App' => 'Illuminate\Support\Facades\App',
...
// 'Event' => 'Illuminate\Support\Facades\Event',
'Event' => 'AntonioRibeiro\Libraries\MyEventFacade',
...
'File' => 'Illuminate\Support\Facades\File',
'ActiveSession' => 'AntonioRibeiro\Facades\ActiveSessionFacade',
),
Don't forget do execute 'composer dump-autoload'.
I don't have access to those Cartalyst Themes, but the error you where receiving was related to the method you didn't created as static:
public function setActive($active)
{
}
Shoud be
public static function setActive($active)
{
}
You'll find some good information about it here (make a class extending the Request "Facade"): http://fideloper.com/extend-request-response-laravel

Related

I wonder how this code works: it is related to classes in PHP

I am working with a framework to build a telegram bot, since I am not a pro in PHP, I am trying to understand the logic behind the codes; there is one thing I cannot understand. then, I decided to ask it here.
The code is much bigger; so I cut some parts of it. I have a specific problem with this method. I don't exactly get how they specified a method for getMessage(). It didn't have any functions with this name in this class.
<?php
/**
* Class Command
*
* Base class for commands. It includes some helper methods that can fetch data directly from the Update object.
*
* #method Message getMessage() Optional. New incoming message of any kind — text, photo, sticker, etc.
abstract class Command
{
/**
* Constructor
*
* #param Telegram $telegram
* #param Update|null $update
*/
public function __construct(Telegram $telegram, ?Update $update = null)
{
$this->telegram = $telegram;
if ($update !== null) {
$this->setUpdate($update);
}
$this->config = $telegram->getCommandConfig($this->name);
}
/**
* Set update object
*
* #param Update $update
*
* #return Command
*/
public function setUpdate(Update $update): Command
{
$this->update = $update;
return $this;
}
/**
* Pre-execute command
*
* #return ServerResponse
* #throws TelegramException
*/
public function preExecute(): ServerResponse
{
if ($this->need_mysql && !($this->telegram->isDbEnabled() && DB::isDbConnected())) {
return $this->executeNoDb();
}
if ($this->isPrivateOnly() && $this->removeNonPrivateMessage()) {
$message = $this->getMessage();
if ($user = $message->getFrom()) {
return Request::sendMessage([
'chat_id' => $user->getId(),
'parse_mode' => 'Markdown',
'text' => sprintf(
"/%s command is only available in a private chat.\n(`%s`)",
$this->getName(),
$message->getText()
),
]);
}
return Request::emptyResponse();
}
I just don't get it how this->getMessage() is working. Is there somebody help me out?
This is a base class, and some other class is extending this class. Since this is an abstract class, it cannot be instantiated.
So you need to look for another class that extends this class (i.e class SomeCommand extends Command). The SomeCommand class in this case will declare the getMessage function.
(Note that SomeCommand is an example, I don't know the real name of the other class.)
Edit:
That function was removed in this commit in favor of using the magic method __call instead. No idea why.

SOAP Client - Class not found

I got a problem using SOAP client in Laravel. I want to send salesorders/customers from an shop to an ERP system. The Structure looks like this: In routes->web.php I call my Controller
Route::get('/show_orders', 'SalesOrdersController#store');
I created an controller SalesOrderController and it looks like this
<?php
namespace App\Http\Controllers;
class SalesOrdersController extends Controller
{
private $soapClient;
public function __construct()
{
$this->middleware('auth');
$this->soapClient = app('soap_client_sales_order');
}
public function store()
{
$soapClient->__setLocation(env('BYD_DOMAIN') . $serviceUrl);
$parameters['BasicMessageHeader'] = array(
'ID' => '00000000000102dcade9bcb0aa000c68',
);
$parameters['Customer'] = array(
'CategoryCode' => '1',
'CustomerIndicator' => 'true',
'Person' => array(
'GivenName' => 'Frank',
'FamilyName' => 'Sent',
),
);
$soapResult = $this->soapClient->MaintainBundle_V1($parameters);
}
}
I created an ServiceProvider SoapServiceProvider – looks like this:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class SoapServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('soap_client_sales_order', function () {
$serviceUrl = '/sap/bc/srt/scs/sap/managecustomerin1';
$wsdlPath = 'soap/managecustomerin1.wsdl';
$soapClient = new \SoapClient(
storage_path($wsdlPath),
array(
'trace' => 1,
'soap_version' => SOAP_1_2,
'exceptions' => 1,
'login' => env('SOAP_USER'),
'password' => env('SOAP_PASSWORD'),
)
);
});
$soapClient->__setLocation(env('BYD_DOMAIN') . $serviceUrl);
return $soapClient;
}
}
When I call my route http://shop.test/show_orders I get the following
Exception:"Class soap_client_sales_order does not exist"
Dump:
/home/vagrant/code/shop/vendor/laravel/framework/src/Illuminate/Container/Container.php
}
/**
* Instantiate a concrete instance of the given type.
*
* #param string $concrete
* #return mixed
*
* #throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function build($concrete)
{
// If the concrete type is actually a Closure, we will just execute it and
// hand back the results of the functions, which allows functions to be
// used as resolvers for more fine-tuned resolution of these objects.
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface of Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
$this->buildStack[] = $concrete;
$constructor = $reflector->getConstructor();
// If there are no constructors, that means there are no dependencies then
// we can just resolve the instances of the objects right away, without
// resolving any other types or dependencies out of these containers.
Hope you can give me a hint.
Thanks,
Manu
Well first of all you never defined $soapClient outside of $app->singleton scope so $soapClient was null and it was a wrong singleton implementation, here is an official guide from laravel docs and the next thing is that you gotta return the singleton inside of singleton scope meaning something like this
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('soap_client_sales_order', function () {
...
...
$soapClient->__setLocation(env('BYD_DOMAIN') . $serviceUrl);
return $soapClient;
});
}

PHP Trait Override Protected Trait Method

I am having problems with a composer package I am dealing with. It implements a trait Billable.
trait Billable
{
/**
* Update the payment method token for all of the user's subscriptions.
*
* #param string $token
* #return void
*/
protected function updateSubscriptionsToPaymentMethod($token)
{
foreach ($this->subscriptions as $subscription) {
if ($subscription->active()) {
BraintreeSubscription::update($subscription->braintree_id, [
'paymentMethodToken' => $token,
]);
}
}
}
}
I am trying to override this method in my class
class Organisation extends Model
{
use Billable;
/**
* Update the payment method token for all of the user's subscriptions.
*
* #param string $token
* #return void
*/
protected function updateSubscriptionsToPaymentMethod($token)
{
foreach ($this->subscriptions as $subscription) {
if ($subscription->active()) {
BrntreeSubscription::update($subscription->braintree_id, [
'paymentMethodToken' => $token,
]);
}
}
}
}
But the method is not overridden. As a test I overrode some of the public functions and they work fine, it this a limitation of traits? I have tried to find the answer online but have come up short.
I am trying to override this function because I need to customize the behaviour of the BraintreeSubscription class.
Any help would be greatly appreciated.
in your class you could do the following notice the T before the function name you may change this to be aliased as anything really.
use billable {
updateSubscriptionsToPaymentMethod as tUpdateSubscriptionsToPaymentMethod;
}
then simply in the class add the desired function:
public function updateSubscriptionsToPaymentMethod(){
...
}

Where do I place my lengthy code that prepares domain objects to be persisted in a Repository?

I have some code like so:
/* Part of Controller::saveAction() */
//create new object instance
$item = new Item();
//populate the Item
$item->setDescription($description);
$item->setQuantity($quantity);
$item->setPrice($price);
//once we have a fully populated object,
//send it to Repository pattern,
//which saves it to persistent storage
$this->repository->saveItem($item);
Imagine several blocks like the above in a single funciton/method, and you will see my issue ... I am pretty happy with my repository line of code, but I do not know where to place all the "prep work" that is done before the call to Repository.
Question:
Where do I place the bulk of code that creates &populates the Item object instance? It is cluttering up my Controller method and I can't seem to imagine another place to put it.
Goal
My goal is to find good structure/design rather than to purely reduce or minimize the number of "item preparation" lines.
Depending on Controller, I have roughly 5-7 Item instances that each have 10-16 lines of code creating and populating the instances.
Observation
Individual values like $description, $quantity, $price must come from somewhere. It could be GET, POST, SESSION, COOKIES, or database or external methods. Let's call it $_SOMEWHERE for clarity. We then get:
$description = $_SOMEWHERE['description'];
$quantity = $_SOMEWHERE['quantity'];
$price = $_SOMEWHERE['price'];
Define an Input&Preparation class that does the work for you and returns the prepared Item.
class AcquireItem
{
function getItem()
{
$item = new Item();
$item->setDescription($_SOMEWHERE['description']);
$item->setQuantity($_SOMEWHERE['quantity']);
$item->setPrice($_SOMEWHERE['price']);
return $item;
}
}
Controller
$item = (new AcquireItem())->getItem();
$this->repository->saveItem($item);
Controller becomes shorter, by effectively "stuffing away" the bulky clutter code into a class that is concerned with reading and preparing input, and decluttering the controller. The code has to exist somewhere, but might as well be out of view, and elsewhere.
For different types of Item, you can vary the method, i.e. getItemA(), getItemB().
As you mentioned about the proper way of achieving
so.. how about using delegators and splitting the controller (blocks) into delegators? It terms of terminology it would be the Facade [Adapter]
add the following to ./module/MyModule/config/module.config.php:
'controllers' => array(
'invokables' => array(
'MyModule\CreateController' => 'MyModule\Controller\MyController',
'MyModule\ReadController' => 'MyModule\Controller\MyController',
),
'delegators' => array(
'MyModule\CreateController' => array(
'MyModule\Controller\Delegator\CreateItemDelegatorFactory'
),
'MyModule\ReadController' => array(
'MyModule\Controller\Delegator\ReadItemDelegatorFactory'
),
),
),
'form_elements' => array(
'invokables' => array(
'item_create' => 'MyModule\Form\CreateForm',
),
),
Create Delegator loads form, populates it, validates and tries to save teh data
./module/MyModule/src/MyModule/Controller/Delegator/CreateItemDelegatorFactory.php:
namespace MyModule\Controller\Delegator;
use Zend\ServiceManager\DelegatorFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use MyModule\Entity\Item as Entity;
/**
* Class loads the form, checks if form been posted and if is valid.
* If form is valid it tries to save the item with repository service
* Class sets the Form per controller, such solution keeps form
* validation messages
*/
class CreateItemDelegatorFactory implements DelegatorFactoryInterface
{
/**
* Determines name of the form to be loaded with formElementManager
*
* #var string
*/
private $form_name = "item_create";
/**
* Name of repository service. It may be database, api or other
*
* #var string
*/
private $service_repository_name = "service.repository";
public function createDelegatorWithName(
ServiceLocatorInterface $serviceLocator,
$name,
$requestedName,
$callback
) {
// assign serviceManager locally
$parentLocator = $serviceLocator->getServiceLocator();
// assign services locally
$routerService = $parentLocator->get('router');
$requestService = $parentLocator->get('request');
// get repository service
$repositoryService = $parentLocator->get($this->service_repository_name);
// read the CreateForm with formElementManager and bind the Entity
$callback->setForm(
$parentLocator->get('FormElementManager')->get($this->form_name)
);
$entity = new Entity;
$callback->getForm($this->form_name)->bind($entity);
// check if data been posted
if($requestService->isPost()) {
$postData = $requestService->getPost($this->form_name);
$callback->getForm($this->form_name)->setData($postData);
// validate form
if($callback->getForm($this->form_name)->isValid()) {
// form is valid
$repositoryService->saveItem($entity);
}
}
}
}
With above Delegator your controller (MyModule\Controller\MyController) would need the additional property and two methods:
/**
* Holds the form object
* #var object
*/
private $form
public function setForm($form=null)
{
$this->form = $form;
return $this;
}
public function getForm()
{
return $this->form;
}
./module/MyModule/src/MyModule/Controller/Delegator/ReadItemDelegatorFactory.php:
namespace MyModule\Controller\Delegator;
use Zend\ServiceManager\DelegatorFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use MyModule\Entity\Item as Entity;
/**
* Creates Delegator which tries read item's id from the (segment type) route
* and read the Item from the repository service
*
*/
class ReadItemDelegatorFactory implements DelegatorFactoryInterface
{
/**
* Item's ID from route
*
* #var string
*/
private $route_identifier = "item_id";
/**
* Name of repository service. It may be database, api or other
*
* #var string
*/
private $service_repository_name = "service.repository";
public function createDelegatorWithName(
ServiceLocatorInterface $serviceLocator,
$name,
$requestedName,
$callback
) {
// assign serviceManager locally
$parentLocator = $serviceLocator->getServiceLocator();
// assign services locally
$routerService = $parentLocator->get('router');
$requestService = $parentLocator->get('request');
// get repository service
$repositoryService = $parentLocator->get($this->service_repository_name);
// get the router match and the item_id
$routerMatch = $routerService->match($requestService);
$itemId = $routerMatch->getParam($this->route_identifier);
// set the data for the target controller
$callback->setItem($repositoryService->readItem($itemId));
return $callback;
}
With above Delegator your controller (MyModule\Controller\MyController) would need the additional property and method:
/**
* Holds the Item object
* #var object \MyModule\Entity\Item
*/
private $item
public function setItem($item=null)
{
$this->item = $item;
return $this;
}
Such a way of controller's use helps for the code to stay DRY and seems to be possible to control the flow. Delegators are being loaded as LIFO, so it is possiblt to preconfigure the controller ($callback) before passing it to another Delegator.
If the ReadController reads the item and the CreateController loads the form it is a short way for UpdateItemDelegator to handle the Item Update task.
'controllers' => array(
'invokables' => array(
'MyModule\UpdateController' => 'MyModule\Controller\MyController',
'delegators' => array(
'MyModule\ReadController' => array(
'MyModule\Controller\Delegator\UpdateItemDelegatorFactory',
'MyModule\Controller\Delegator\CreateItemDelegatorFactory',
'MyModule\Controller\Delegator\ReadItemDelegatorFactory'
),
),
),
Delegators explained:
http://ocramius.github.io/blog/zend-framework-2-delegator-factories-explained/
Edit:
Controller prepared for both Delegators (Create and Read) would look like:
namespace MyModule\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class CreateController extends AbstractActionController
{
/**
* Holds the Item object
* #var object \MyModule\Entity\Item
*/
private $item
/**
* Holds the form object
* #var mixed null|object
*/
private $form;
/**
* Item's details. It reads item by the `item_id` which is param set in route with same name
* #return \Zend\View\Model\ViewModel
*/
public function readAction()
{
$v = new ViewModel();
// set item, access it in template as `$this->item`
$v->setVariable('item',$this->getItem());
return $v;
}
/**
* The Form preconfigured with CreateItemDelegatorFactory should be av. in template as `$this->form`
* #return \Zend\View\Model\ViewModel
*/
public function createAction()
{
$v = new ViewModel();
// set form, access the in template as `$this->form`
$v->setVariable('form',$this->getForm());
return $v;
}
/**
* Sets the Item object
* #var $item \MyModule\Entity\Item
* #return $this
*/
public function setItem($item=null)
{
$this->item = $item;
return $this;
}
/**
* Gets the Item object
* #return object \MyModule\Entity\Item
*/
public function getItem()
{
return $this->item;
}
public function setForm($form=null)
{
$this->form = $form;
return $this;
}
/**
* Returns form defined in config and CreateItemDelegatorFactory::form_name
* #return \Zend\Form\Form
*/
public function getForm()
{
return $this->form;
}
}
I personally believe that create-methods should go into the repository.
That is because I would expect the repositories to contain all CRUD (create, read, update, delete) methods.
It's just my personal thought on this topic...

Laravel extend Form class

I'm trying to extend the Form class in L4.1 but I seem to be missing something. My file is named FormBuilder.php based on the API and is saved in app/libraries/extended/FormBuilder.php.
<?php namespace Extended;
class FormBuilder extends \Illuminate\Html\FormBuilder {
/**
* Create a text input field.
*
* #param string $name
* #param string $value
* #param array $options
* #return string
*/
public function text($name, $value = null, $options = array())
{
$options = $options + array('id'=>"field-{$name}");
return $this->input('text', $name, $value, $options);
}
}
This is actually the first time I've tried extending a core class in Laravel. I can't seem to put my finger on how to properly extend core classes like this Form class.
Edit:
I added "app/libraries/extended" to my composer.json file and ran both composer.phar update and composer.phar dump-autoload but it still seemed to be using the core class instead of my extended one. What am I forgetting to do?
To extend/swap a Laravel core class, you can create a Service Provider:
File: app/App/Libraries/Extensions/FormBuilder/FormBuilderServiceProvider.php
<?php namespace App\Libraries\Extensions\FormBuilder;
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;
use App\Libraries\Extensions\FormBuilder\FormBuilder;
class FormBuilderServiceProvider extends IlluminateServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->bindShared('formbuilder', function($app)
{
$form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken());
return $form->setSessionStore($app['session.store']);
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return array('formbuilder');
}
}
Create a Facade for it:
File: app/App/Libraries/Extensions/FormBuilder/FormBuilderFacade.php
<?php namespace App\Libraries\Extensions\FormBuilder;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
class FormBuilderFacade extends IlluminateFacade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'formbuilder'; }
}
This would be your namespaced service class:
File: app/App/Libraries/Extensions/FormBuilder/FormBuilder.php
<?php namespace App\Libraries\Extensions\FormBuilder;
use \Illuminate\Html\FormBuilder as IlluminateFormBuilder;
class FormBuilder extends IlluminateFormBuilder {
public function text($name, $value = null, $options = array())
{
$options = $options + array('id'=>"field-{$name}");
return $this->input('text', $name, $value, $options);
}
}
Open app/config/app.php and your Service Provider to the list
'App\Libraries\Extensions\FormBuilder\FormBuilderServiceProvider',
And replace Laravel's Form alias with yours
'Form' => 'App\Libraries\Extensions\FormBuilder\FormBuilderFacade',
To test I created a router like this:
Route::any('test', function() {
return e(Form::text('first_name'));
});
And it gave me this result:
<input id="field-first_name" name="first_name" type="text">

Categories