I have created 2 basic shipping methods in 2 different plugins, each plugin are below, one is Royal Mail the other is Courier. They both show up in the drop down in the shipping zone:
I can then add say Royal Mail to the zone, but if i now come to add Courier as well, Royal Mail disappears and vice versa, it's only letting me add one of my custom methods at a time and i'm stumped as to why.
The next problem is, when i delete one of my custom methods and hit save, i'm getting this fatal error and it doesn't remove, as far as i can see both of these classes are done correctly to the point they are at:
[14-Dec-2021 13:54:59 UTC] PHP Fatal error: Uncaught Error: Call to a
member function get_instance_option_key() on bool in
/Volumes/Work/project/public_html/wp-content/plugins/woocommerce/includes/class-wc-ajax.php:2923
add_action( 'woocommerce_shipping_init', function() : void {
/**
* Class Utterly_Courier_Shipping_Method
*/
class Utterly_Courier_Shipping_Method extends WC_Shipping_Method
{
/**
* Utterly_Royal_Mail_Shipping_Method constructor.
*/
public function __construct()
{
$this->id = 'utterly_courier';
$this->method_title = 'Courier delivery (Tracked & Signed)';
$this->method_description = 'Shipping via Courier';
$this->supports = [
'shipping-zones'
];
$this->init();
$this->enabled = 'yes';
$this->title = 'Courier delivery (Tracked & Signed)';
}
/**
*
*/
public function init() : void
{
$this->init_form_fields();
$this->init_settings();
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
*
*/
public function init_form_fields() : void
{
parent::init_form_fields();
}
/**
* #param array $package
*/
public function calculate_shipping($package = [])
{
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => 10
);
$this->add_rate( $rate );
}
}
});
add_filter( 'woocommerce_shipping_methods', function( array $methods ) : array {
$methods['utterly_courier'] = 'Utterly_Courier_Shipping_Method';
return $methods;
});
add_action( 'woocommerce_shipping_init', function() : void {
/**
* Class Utterly_Royal_Mail_Shipping_Method
*/
class Utterly_Royal_Mail_Shipping_Method extends WC_Shipping_Method
{
/**
* Utterly_Royal_Mail_Shipping_Method constructor.
*/
public function __construct()
{
$this->id = 'utterly_royal_mail';
$this->method_title = 'Royal Mail 1st Class (Tracked)';
$this->method_description = 'Shipping via Royal Mail';
$this->supports = [
'shipping-zones'
];
$this->init();
$this->enabled = 'yes';
$this->title = 'Royal Mail 1st Class (Tracked)';
}
/**
*
*/
public function init() : void
{
$this->init_form_fields();
$this->init_settings();
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, [ $this, 'process_admin_options' ] );
}
/**
*
*/
public function init_form_fields() : void
{
parent::init_form_fields();
}
/**
* #param array $package
*/
public function calculate_shipping($package = [])
{
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => 10
);
$this->add_rate( $rate );
}
}
});
add_filter( 'woocommerce_shipping_methods', function( array $methods ) : array {
$methods['utterly_royal_mail'] = 'Utterly_Royal_Mail_Shipping_Method';
return $methods;
});
Related
I added this class to my plugin directory, but when I refresh my dashboard, the admin page doesn't show up and I do not get any errors. Am I doing something wrong in the __construct() method?
class AA_Admin_Page {
/**
* Title of the page.
*/
public $page_title;
/**
* Text to be used for the menu.
*/
public $menu_title;
/**
* Unique slug for the menu page.
*/
public $slug;
function __construct() {
$this->$page_title = __( 'AA Plugin Page', 'AA-plugin' );
$this->$menu_title = __( 'AA Plugin Page', 'AA-plugin' );
$this->slug = 'aa_admin_page';
$this->render_page();
}
public function render_page() {
add_action( 'admin_init', [ $this, 'aa_admin_page' ] );
}
public function aa_admin_page() {
add_menu_page(
$this->$page_title,
$this->$menu_title,
'manage_options',
$this->$slug,
[ $this, 'admin_page_callback' ],
);
}
public function admin_page_callback() {
echo "This page works!";
}
}
new AA_Admin_Page();
I want to add shipping fees depends on the customer address after he inserts his address, the value of shipping calculted by API request that I'll replace in the following class but the method itself not added at all.
this code in shipping.php file then I included in the index.php file of the Plugin. I also tried different code snippets all of them not working and the shipping method doesn't even added
function trucking_shipping_init()
{
if (!class_exists('WC_Your_Shipping_Method')) {
class WC_trucking_shipping_Method extends WC_Shipping_Method
{
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct()
{
$this->id = 'trucking_shipping_method'; // Id for your shipping method. Should be uunique.
$this->method_title = __('JNE TRUCKING'); // Title shown in admin
$this->method_description = __('Pengiriman dengan truck JNE'); // Description shown in admin
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->title = "JNE TRUCKING"; // This can be added as an setting but for this example its forced.
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init()
{
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
/**
* calculate_shipping function.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping($package = array())
{
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => '10.99', // this will be replaced by returned API fees
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate($rate);
}
}
}
add_action('woocommerce_shipping_init', 'trucking_shipping_init');
function add_trucking_shipping_method($methods)
{
$methods['trucking_shipping_method'] = 'WC_trucking_shipping_Method';
return $methods;
}
add_filter('woocommerce_shipping_methods', 'add_trucking_shipping_method');
}
I'm currently working on a custom shipping method for WooCommerce. The goal is to calculate the shipping based on the adress the customer enters in the order process. We currently have an API that receives a storeId and the adress of a customer and returns back the exact amount of shipping we need to apply to the order.
For this, I've started to write a new custom shipping order plugin. I am already receiving the $packages array with the order, however I can't seem to find a way to receive the customers adress in the calculate_shipping method. There also will be a useraction required to select the prefered warehouse on the reviewOrderBeforePayment page and I am not quite sure how to access that data yet.
<?php
/*
* Plugin Name: Warehouses Integration
* Description: WooCommerce integration of the Warehouses API
* Version: 1.1.0
* Author: -
* Author URI: -
* Developer: Laura Heimann
* Developer URI: -
* Text Domain: woocommerce-extension
* WC requires at least: 4.0.1
* WC tested up to: 4.0.1
*/
// Security Measure
if (!defined('ABSPATH')) {
exit;
}
// Check if WooCommerce is installed
if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option( 'active_plugins')))) {
alert("WooCommerce is not installed!");
}
// Main Render Function
// This will be hooked into the WooCommerce Order Page
function wcfw_render() {
// TODO: Show List of possible stores, let user select and save selected store somehow
echo "WCFWRENDER";
}
function wcfw_renderThankYou() {
echo "WCFWTHANKYOU";
}
function wcfw_renderReviewOrder() {
// TODO: Show warning if items from different stores
echo "WCFWREVIEWORDER";
}
function wcfw_shippingMethodInit() {
if ( ! class_exists( 'WC_WCFW_Shipping_Method' ) ) {
class WC_WCFW_Shipping_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct() {
$this->id = 'wcfw_shipping_method';
$this->title = __( 'Warehouses Shipping' );
$this->method_description = __( 'Shipping Method through the Warehouse' );
$this->enabled = "yes";
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Settings
*/
function init_form_fields() {
$this->form_fields = array(
'apiBase' => array(
'title' => __('Api Base'),
'type' => 'text',
'description' => __('The base URL for API-Calls.'),
'default' => __('---')
),
);
}
/**
* calculate_shipping function.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package = array() ) {
var_dump( $package );
// TODO: Get Customers Adress and selected store
// TODO: Send data to API and apply provided shipping
$rate = array(
'label' => __('Shipping by Truck'),
'cost' => '10.99',
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
}
}
}
function add_wcfw_shipping_method( $methods ) {
$methods[] = 'WC_WCFW_Shipping_Method';
return $methods;
}
// Hooks
add_action('woocommerce_review_order_before_payment', 'wcfw_render', 10);
add_action('woocommerce_thankyou', 'wcfw_renderThankYou', 10);
add_action('woocommerce_after_cart_contents', 'wcfw_renderReviewOrder', 10);
add_action('woocommerce_shipping_init', 'wcfw_shippingMethodInit');
add_filter('woocommerce_shipping_methods', 'add_wcfw_shipping_method');
I used the code below (as provided by woocommerce API) to add custom Shipping Method and it is working but now I want to add another shipping method I tried copy pasting same code with different class name but it doesn't work actually the second method is replacing the first one
I want to know how can I create another Shipping method?
Thank you
function your_shipping_method_init() {
if ( ! class_exists( 'WC_Your_Shipping_Method' ) ) {
class WC_Your_Shipping_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct() {
$this->id = 'vip_rate'; // Id for your shipping method. Should be uunique.
$this->method_title = __( 'VIP Shipping Rate' ); // Title shown in admin
$this->method_description = __( '$35 flate rate' ); // Description shown in admin
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->title = "VIP Shipping rate"; // This can be added as an setting but for this example its forced.
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields(); // This is part of the settings API. Override the method to add your own settings
$this->init_settings(); // This is part of the settings API. Loads settings you previously init.
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* calculate_shipping function.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package ) {
$cost=35;
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => round($cost,2),
'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'your_shipping_method_init' );
function add_your_shipping_method( $methods ) {
$methods[] = 'WC_Your_Shipping_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_your_shipping_method' );
I'm having the same issue , my solution was made a new class who extends from WC_Shipping_Method and keep all the same code there and i made 3 new classes extending the new one , any one with their own ID and mothod type
Its not the best solution but its more legant than duplicate N times the same code of the class
OK I have succeeded in adding another Shipping method by renaming Class name.Previously I may be doing something wrong
However I would like to know if there was some better way of doing it because I have copy pasted whole chunk of code twice , My background is not in OOP however I think it is not the proper way of doing this thing
I am using the Silverstripe Swipestripe module for an online store. Because of the number of products the client has, it's not practical to nave them navigate to each individual product through the site tree when they want to make changes to a product (which happens fairly regularly) so I'd like to have a modeladmin to list all products and allow them to search for a product by name/stockcode.
I thought this would be solvable in the same way as DataObjects (and searches seem to suggest that people have done achieved this), however when I navigate to products in ModelAdmin view, I get:
Fatal Error : Call to a member function stat() on a non-object in
/path/to/folder/wwwroot/framework/model/DataObject.php on line 3192
<?php
class ProductAdmin extends ModelAdmin
{
private static $managed_models = array('Product');
private static $url_segment = 'product';
private $menu_title = 'Products';
}
Interestingly, pages and other extensions of the page class do work.
Here is the code for the Product class:
class Product extends Page {
/**
* Flag for denoting if this is the first time this Product is being written.
*
* #var Boolean
*/
protected $firstWrite = false;
/**
* DB fields for Product.
*
* #var Array
*/
private static $db = array(
'Price' => 'Decimal(19,4)',
'Currency' => 'Varchar(3)',
'StockCode' => 'Varchar(255)',
'Stock' => 'Int',
'Featured' => 'Boolean',
'YouTubeID' => 'Varchar(255)'
);
/**
* Actual price in base currency, can decorate to apply discounts etc.
*
* #return Price
*/
public function Amount() {
// TODO: Multi currency
$shopConfig = ShopConfig::current_shop_config();
$amount = new Price();
$amount->setAmount($this->Price);
$amount->setCurrency($shopConfig->BaseCurrency);
$amount->setSymbol($shopConfig->BaseCurrencySymbol);
//Transform amount for applying discounts etc.
$this->extend('updateAmount', $amount);
return $amount;
}
/**
* Display price, can decorate for multiple currency etc.
*
* #return Price
*/
public function Price() {
$amount = $this->Amount();
//Transform price here for display in different currencies etc.
$this->extend('updatePrice', $amount);
return $amount;
}
/**
* Has many relations for Product.
*
* #var Array
*/
private static $has_many = array(
'Attributes' => 'Attribute',
'Options' => 'Option',
'Variations' => 'Variation'
);
/**
* Defaults for Product
*
* #var Array
*/
private static $defaults = array(
'ParentID' => -1,
'Stock' => 999
);
/**
* Summary fields for displaying Products in the CMS
*
* #var Array
*/
private static $summary_fields = array(
'Amount.Nice' => 'Price',
'Title' => 'Title'
);
private static $searchable_fields = array(
'Title' => array(
'field' => 'TextField',
'filter' => 'PartialMatchFilter',
'title' => 'Name'
)
);
/**
* Set firstWrite flag if this is the first time this Product is written.
*
* #see SiteTree::onBeforeWrite()
* #see Product::onAfterWrite()
*/
public function onBeforeWrite() {
parent::onBeforeWrite();
if (!$this->ID) $this->firstWrite = true;
//Save in base currency
$shopConfig = ShopConfig::current_shop_config();
$this->Currency = $shopConfig->BaseCurrency;
}
/**
* Unpublish products if they get deleted, such as in product admin area
*
* #see SiteTree::onAfterDelete()
*/
public function onAfterDelete() {
parent::onAfterDelete();
if ($this->isPublished()) {
$this->doUnpublish();
}
}
/**
* Set some CMS fields for managing Products
*
* #see Page::getCMSFields()
* #return FieldList
*/
public function getCMSFields() {
$shopConfig = ShopConfig::current_shop_config();
$fields = parent::getCMSFields();
//Product fields
$fields->addFieldToTab('Root.Main', new PriceField('Price'), 'Content');
$fields->addFieldToTab('Root.Main', new TextField('StockCode'), 'Price');
$fields->addFieldToTab('Root.Main', new TextField('Stock'), 'Price');
$fields->addFieldToTab('Root.Main', new CheckBoxField('Featured'), 'Content');
$fields->addFieldToTab('Root.Images', new TextField('YouTubeID', 'YouTube Video ID (Taken from the end of the video url. ie https://www.youtube.com/watch?v=ABC123 would be ABC123)'));
//Replace URL Segment field
if ($this->ParentID == -1) {
$urlsegment = new SiteTreeURLSegmentField("URLSegment", 'URLSegment');
$baseLink = Controller::join_links(Director::absoluteBaseURL(), 'product/');
$url = (strlen($baseLink) > 36) ? "..." .substr($baseLink, -32) : $baseLink;
$urlsegment->setURLPrefix($url);
$fields->replaceField('URLSegment', $urlsegment);
}
if ($this->isInDB()) {
//Product attributes
$listField = new GridField(
'Attributes',
'Attributes',
$this->Attributes(),
GridFieldConfig_BasicSortable::create()
);
$fields->addFieldToTab('Root.Attributes', $listField);
//Product variations
$attributes = $this->Attributes();
if ($attributes && $attributes->exists()) {
//Remove the stock level field if there are variations, each variation has a stock field
$fields->removeByName('Stock');
$variationFieldList = array();
foreach ($attributes as $attribute) {
$variationFieldList['AttributeValue_'.$attribute->ID] = $attribute->Title;
}
$variationFieldList = array_merge($variationFieldList, singleton('Variation')->summaryFields());
$config = GridFieldConfig_HasManyRelationEditor::create();
$dataColumns = $config->getComponentByType('GridFieldDataColumns');
$dataColumns->setDisplayFields($variationFieldList);
$listField = new GridField(
'Variations',
'Variations',
$this->Variations(),
$config
);
$fields->addFieldToTab('Root.Variations', $listField);
}
}
//Ability to edit fields added to CMS here
$this->extend('updateProductCMSFields', $fields);
if ($warning = ShopConfig::base_currency_warning()) {
$fields->addFieldToTab('Root.Main', new LiteralField('BaseCurrencyWarning',
'<p class="message warning">'.$warning.'</p>'
), 'Title');
}
return $fields;
}
/**
* Get the URL for this Product, products that are not part of the SiteTree are
* displayed by the {#link Product_Controller}.
*
* #see SiteTree::Link()
* #see Product_Controller::show()
* #return String
*/
public function Link($action = null) {
if ($this->ParentID > -1) {
return parent::Link($action);
}
return Controller::join_links(Director::baseURL() . 'product/', $this->RelativeLink($action));
}
/**
* A product is required to be added to a cart with a variation if it has attributes.
* A product with attributes needs to have some enabled {#link Variation}s
*
* #return Boolean
*/
public function requiresVariation() {
$attributes = $this->Attributes();
return $attributes && $attributes->exists();
}
/**
* Get options for an Attribute of this Product.
*
* #param Int $attributeID
* #return ArrayList
*/
public function getOptionsForAttribute($attributeID) {
$options = new ArrayList();
$variations = $this->Variations();
if ($variations && $variations->exists()) foreach ($variations as $variation) {
if ($variation->isEnabled()) {
$option = $variation->getOptionForAttribute($attributeID);
if ($option) $options->push($option);
}
}
$options = $options->sort('SortOrder');
return $options;
}
/**
* Validate the Product before it is saved in {#link ShopAdmin}.
*
* #see DataObject::validate()
* #return ValidationResult
*/
public function validate() {
$result = new ValidationResult();
//If this is being published, check that enabled variations exist if they are required
$request = Controller::curr()->getRequest();
$publishing = ($request && $request->getVar('action_publish')) ? true : false;
if ($publishing && $this->requiresVariation()) {
$variations = $this->Variations();
if (!in_array('Enabled', $variations->map('ID', 'Status')->toArray())) {
$result->error(
'Cannot publish product when no variations are enabled. Please enable some product variations and try again.',
'VariationsDisabledError'
);
}
}
return $result;
}
}
Can anyone suggest what I'm doing wrong here or an alternative way to do what I'm trying to achieve?
Cheers
Just in case anyone still having the same problem.
So, it happens if you have installed Product Categories module for swipestripe.
The cause of this is private static $searchable_fields in ProductCategory_Extension class in ProductCategory.php file in that module. Just comment out that field and it will work.
It is because the dataobject class tries to stat Category as a class - which obviously doesnt exist.
I will fix it and push to github if I get some time. Just wanted to update here so that others dont waste time scratching head why it doesnt work.