I'm trying to extend the customer endpoint in woocommerce api to include some customfields I've created in my functions.php.
But I can't understand how to do it.
I have copied the class-wc-rest-customers-controller.php from the woocommerce plugin (woocommerce/includes/api/) to my woocommerce-folder in my theme as you should do with woocommerce files when you want to edit them.
This is how my class-wc-rest-customers-controller.php looks like:
https://www.pastebucket.com/561405
My plan now was to edit this copy of the file to include my custom_fields I've added in functions.php. But I can't solve this part.
This is the code from my functions.php that I added my custom fields with:
https://www.pastebucket.com/561406
It feels like it is in the function at line 475 in class-wc-rest-customers-controller.php, but I'm not sure.
So I'm wondering where and how should I add my custom fields to this class-wc-rest-customers-controller.php or I'm all wrong about this?
The file overrides apply AFAIK for templates only. In this case you are trying to override a class, which is different.
As you wrote, it's not a good idea to make your changes directly to the file inside WooCommerce directory. In fact, I wouldn't recommend changing the native endpoint at all, except through actions & filters.
One good, re-usable and future-proof way to change the behavior of a WC REST API endpoint would be to create your own endpoint which simply extends the Woocommerce controller class, and overrides the methods which need to be customized. Preferably, try not to override the entire method, but include the call to the parent method in your custom method.
Example solution: (haven't tested this particular one, but made a very similar one recently)
wp-content/plugins/
woocommerce/
your-plugin/
includes/
class-your-plugin.php
your-plugin.php
your-plugin.php
<?php
defined( 'ABSPATH' ) || exit;
add_action('rest_api_init', function(){
require_once __DIR__ . '/includes/class-your-plugin.php';
$controller = new Your_Custom_Class();
$controller->register_routes();
} );
includes/class-your-plugin.php
<?php
defined( 'ABSPATH' ) || exit;
/**
* Class Your_Custom_Class
*/
class Your_Custom_Class extends WC_REST_Customers_Controller {
/**
* Endpoint namespace.
* Use the wc- prefix to make use of WooCommerce authentication for third-parties.
* #see /wp-content/plugins/woocommerce/includes/api/class-wc-rest-authentication.php:63
*
* #var string
*/
protected $namespace = 'wc-your-route/v1';
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/your-custom-route',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'your_customized_function' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
),
'schema' => array( $this, 'get_item_schema' ),
)
);
}
/**
* Your custom function
*/
protected function your_customized_function( $arg1, $arg2) {
/*******************************
// IMPLEMENT CUSTOM CODE HERE
*******************************/
parent::the_name_of_the_original_function( $arg1, $arg2 );
/*******************************
// IMPLEMENT CUSTOM CODE HERE
*******************************/
}
This way you can freely extend the API for your own needs, take advantage of all current and future features of the WC API, and preserve the native API... well.. native.
While this should be a clean and "correct" solution, I would not recommend going for this path without a solid understanding of PHP classes and inheritance, as well as a good IDE to work with.
So it seems I managed to solve it.
I couldn't overwrite the class-wc-rest-customers-controller.php by copying it to my-theme-folder/woccomerce/includes/api/
So instead I just overwrote it and kept a backup of the original. But I now also need to keep an backup of of the class-wc-rest-customers-controller.php file I overwrote with.
This isn't the correct way of doing it, but this was the only way I could sole my problem.
UPDATE: Seems like I cannot update these values through the api. So this was not a solution at all.
Related
I am trying to modify a Wordpress plugins' method without changing the plugin core code itself. I need to set some status flag in a different part of the website.
The method is defined in the plugin class like so:
add_action('plugins_action_name', [$this, 'local_method_name']);
The plugin method does not offer any actions or filters to hook into.
If this wasn't Wordpress but plain PHP I would write my own class, which extends the plugins' original class but has it's own method by the same name - but in Wordpress this gets me nowhere since I just move the problem from one plugin file to the next.
Is there a way to remove the plugins action altogether? And then redefine my own version of it?
Since the add_action has been called with $this - how would I remove it in some functions.php file?
Thanks
UPDATE
Indeed I want to learn how to handle different scenarios. Not expecting a one size fits all solution, but I do need to know how to avoid core changes, as each core-change complicates my deploy/update process exponentially.
Sure I will add some more details. My example plugin is WooCommerce Germanized Pro which comes with a WC_GZDP_Download_Handler::download() method which I needed to modify, in order to set some sort of "Warehouse downloaded the invoice" flag for each order. Which is later used to highlight new orders, filter orders that have been handed over to fulfilment etc.
There seems to be a Invoice Helper class which does in fact have a singleton pattern.
my modification in the download class is trivial:
public static function download( $pdf, $force = false ) {
... /* original code above, now my modifications */
if ($check_some_codition) {
$invoice = $pdf->get_invoice();
update_post_meta($invoice->get_id(), 'some-flag', 'some-value');
}
/* then resume original function (return the file to the browser) */
self::out( $filename, $file, $force );
}
I am trying to unhook and modify an action from within my child themes functions.php file.
The WordPress plugin Sensei, adds this action in line 86 of this document.
https://github.com/Automattic/sensei/blob/master/includes/class-sensei-modules.php#L86
The action references a function further down the page that is responsible for outputting a dynamic header element.
/**
* Show the title modules on the single course template.
*
* Function is hooked into sensei_single_course_modules_before.
*
* #since 1.8.0
* #return void
*/
public function course_modules_title( ) {
if( sensei_module_has_lessons() ){
echo '<header><h2>' . __('Modules', 'woothemes-sensei') . '</h2></header>';
}
}
My goal here is to change the html currently output as 'Modules' to something else.
I have tried to the following in my child themes functions.php file but neither seem to be working.
remove_action( 'sensei_single_course_modules_before', array( 'Sensei_Core_Modules', 'course_modules_title' ), 20);
remove_action( 'sensei_single_course_modules_before', array( 'Sensei()->Sensei_Core_Modules', 'course_modules_title' ), 20);
The issue is, I do not know how to determine which initial parameter, to add to the array to call the correct class. Because I am accessing it externally I cannot use $this like it is being used in the core file.
In order to remove the action you have to find the instance of the class. This is an assumption since I don't have access to the source code of Sensei but big chance there is one since most WordPress plug-ins use this method.
When you find the instance name you can load it using global $senseiInstance - replace this with the actual name of the variable.
Then you can remove the action using for example this code:
remove_action( 'sensei_single_course_modules_before', array( $senseiInstance, 'course_modules_title' ), 20);
More information can be found in for example this article: https://www.sitepoint.com/digging-deeper-wordpress-hooks-filters.
Hope this helps you out!
I'm trying to use and extend the WP-API for Wordpress.
Now I might just be stupid but I really can't figure out where to start even though I've read the link above over and over. And I'm not talking code-wise but the very basics, where do I put the code? In a plugin? If so, what do I need to include to get it to work? Or is it enough to extend the class?
Sorry but I just find the info on the page to be way too little... Or have I totaly missed an perfectly structured example from top to bottom?
Here it is on GitHub.
Thanks for any help!
I don't know if the documentation was out of date or something but it's pretty simple to extend WP-API. You'll need to write a plugin first.
In the plugin, where you register the hooks like scripts and styles (functions.php, bootstrap.php) you add a new hook to register the routes.
add_filter( 'json_endpoints', array( $this, 'registerRoutes' ) );
public function registerRoutes($routes){
$editorService = $this->container["editorService"];
$routes['/newsletters'] = array(
array( array( $editorService, 'create'), \WP_JSON_Server::CREATABLE | \WP_JSON_Server::ACCEPT_JSON ),
);
$routes['/newsletters/(?P<id>\d+)'] = array(
array( array( $editorService, 'get'), \WP_JSON_Server::READABLE )
);
return $routes;
}
If you read the documentation you'll see that newsletter is the entity. In this example i inject a service and call it in the routes. Very probably you use a different approach and if you've difficulties in this point you'll have to figure out how to structure the plugin, which patterns apply, write or not your own framework, etc.
If thats the case check this skeleton, it's a great approach to MVC https://github.com/iandunn/WordPress-Plugin-Skeleton
If you want to call a function inside the same class you would do:
public function registerRoutes(){
$routes['/newsletters'] = array(
array( array( $this, 'createNewsletter'), \WP_JSON_Server::CREATABLE | \WP_JSON_Server::ACCEPT_JSON )
);
}
public function createNewsletter() {
$wpdb->prepare(); // etc etc
}
I must place a button in order view that will send further the order id. The id will be used for a simple database query then it should return to order view.
I found out how to create the button, I created app/code/local/Mage/Adminhtml/Block/Sales/Order/View.php following the view.php from core.
Here is my code :
class Mage_Adminhtml_Block_Sales_Order_View extends
Mage_Adminhtml_Block_Widget_Form_Container
{
public function __construct()
{
$this->_objectId = 'order_id';
$this->_controller = 'sales_order';
$this->_mode = 'view';
parent::__construct();
$this->_removeButton('delete');
$this->_removeButton('reset');
$this->_removeButton('save');
$this->setId('sales_order_view');
$order = $this->getOrder();
$this->_addButton('release_payment', array(
'label' => Mage::helper('sales')->__('Release Payment'),
'onclick' => 'setLocation(\'' . $this->getUrl('*/*/release') . '\')',
'class' => 'go'
));
}
What I want is that this location that should be sales_order/release to actually do something with the order id. I tried to understand how but I can't manage to actually create the controller.
You should probably collect those overrides into a module. The ugly method would otherwise be to copy
app/code/core/Mage/Adminhtml/controllers/OrderController.php
to
app/code/local/Mage/Adminhtml/controllers/OrderController.php
and add the releaseAction()-function to it.
For the more elegant solution you would need to create a module with config options in it's etc/config.xml file to override/overload the specified block and controller code. With this method your changes are better contained and it's easy to switch the module on/off. Also when extending the original block/controller you don't need to include anything other then the modified/added methods, the rest will be executed from the original file.
Actually even the main documentation from Magento has decent examples:
http://www.magentocommerce.com/wiki/5_-_modules_and_development/0_-_module_development_in_magento/how_to_overload_a_controller
And Googling for magento controller overloadgives good results too, so I won't give an lengthy example right here.
Is it possible to create your own hook in a Drupal module for other Drupal modules to consume? If not, is there a mechanism in Drupal for third party developers to provide hooks? If everything's been a no so far, where in the core are the list of hooks implemented?
As I understand things, Drupal modules work on a event like system called hooks. When you create a new module, you create functions that implement a hook. For example, there's a hook_delete hook. If you implement a function in your module
function mymodule_delete($node)
{
}
this function will be called whenever a node is deleted.
What I want to know is, is there a way or me, as a third party module developer, to create my own hooks. Say, something like hook_alanskickbutthook so that other module developers could subscribe to this hook.
If this is possible, how do you do it? I've looked around the official docs and haven't found much there, and I still get a little dizzy when I start poking around the Drupal source code (I understand recursion, but don't spend enough time thinking about recursive problems). Full solutions are welcome, but I'm happy to just be pointed in the right direction.
Module_invoke_all() is your ticket to creating your own hooks:
see the API:
http://api.drupal.org/api/drupal/includes--module.inc/function/module_invoke_all
and then look at this great writeup:
http://web.archive.org/web/20101227170201/http://himerus.com/blog/himerus/creating-hooks-your-drupal-modules
(edit: was at http://himerus.com/blog/himerus/creating-hooks-your-drupal-modules but this is now gone)
Once you've made your hook, it can be called in another module using:
/**
* Implementation of hook_myhookname()
*/
function THISMODULENAME_myhookname(args){
//do stuff
}
For example, say you wanted to create hook_my_custom_goodness() for others to use. Then just place code like this in your module at the point where you want the hook to happen:
$variables['msg'] = 'foo';
// Make sure at least one module implements our hook.
if (sizeof(module_implements('my_custom_goodness')) > 0) {
// Call modules that implement the hook, and let them change $variables.
$variables = module_invoke_all('my_custom_goodness', $variables);
}
drupal_set_message($variables['msg']); // Will display 'bar' instead.
Now, if anybody wanted to use your hook, then they could do so in their own module like this:
/**
* Implements hook_my_custom_goodness().
*/
function SOME_OTHER_MODULE_my_custom_goodness($variables) {
$variables['msg'] = 'bar';
return $variables;
}
There is a more complete explanation here:
http://tylerfrankenstein.com/code/drupal-create-custom-hook-for-other-modules
You need to implement two hooks 1. hook_token_info() & 2. hook_tokens() in your module file Below i have given code to create my custom token "query-param-all" and used that token in views- Textarea field.....
/**
* Implements hook_token_info().
*/
function mycustom_token_info() {
$type = [
'name' => ('Custom Token'),
'description' => ('Tokens for custom things.'),
];
$node['query-param-all'] = [
'name' => ("Get all URL query string"),
'description' => ('Get all URL query string'),
];
return [
'types' => ['customtoken' => $type],
'tokens' => ['customtoken' => $node],
];
}
/**
* Implements hook_tokens().
*/
function mycustom_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
$replacements = [];
//print '<pre>'; print_r($data);exit;
$current_path = \Drupal::request()->query->all();
$query_param = '';
if( count($current_path) > 0) {
$amper = '';
$query_param = '?';
foreach($current_path as $key => $value){
$query_param .= $amper.$key.'='.$value;
$amper = '&';
}
}
if ($type == 'customtoken') {
foreach ($tokens as $name => $original) {
switch ($name) {
case 'query-param-all':
$replacements[$original] = $query_param;
break;
}
}
}
return $replacements;
}
If i recall... http://api.drupal.org/api/drupal/modules--node--node.api.php/function/hook_delete/7
does ths help? been a while since I messed with Drupal.
To create/offer custom Drupal hook, you must implement in a ways such that calling the hook with module_invoke or module_invoke_all does not make any conflicts with other module hooks. The name of the hook should be unique and it should offer all/specific feature in such a general way that it doesn't require any type of adjustments with code. All the configuration must go on admin pages and should store those configurations in a separate table or any existing tables create by Drupal or modules on which your modules depends. The hook should be easy to implment by other modules and it should not be much complex to implement. When you create custom hooks, your module(s) act(s) as API provider.
For Drupal 6 & 7, drupal_alter() is probably the best option.
As stated in the module_invoke_all() documentation,
All arguments are passed by value. Use drupal_alter() if you need to
pass arguments by reference.
In Drupal 8, use ModuleHandler::alter.
Passes alterable variables to specific hook_TYPE_alter()
implementations.