I need rewrite the values from a hash of hashes into parallel arrays.
Specifically, I want to transform
my %cars;
$cars{123} = { owner => "Mark", color => "black", maxspeed = "260" };
$cars{124} = { owner => "Jason", color => "red", maxspeed => "230" };
$cars{125} = { owner => "Sumaya", color => "pink", maxspeed => "200" };
into
my #owners = ( "Mark", "Jason", "Sumaya" );
my #colors = ( "black", "red", "pink" );
my #maxspeeds = ( "260", "230", "200" );
This can be achieved using
my #ids = keys(%cars);
my #owners = map { $_->{ owner } } #cars{#ids};
my #colors = map { $_->{ color } } #cars{#ids};
my #maxspeeds = map { $_->{ maxspeed } } #cars{#ids};
or
my (#ids, #owners, #colors, #maxspeeds);
while (my ($id, $car) = each(%$cars)) {
push #ids, $id;
push #owners, $car->{owner};
push #colors, $car->{colors};
push #maxspeeds, $car->{maxspeeds};
}
This is a weird request because one normally avoids parallel arrays because using them leads to more complicated code.
Related
I'm building a Flutter app linked with WooCommerce through REST-API, and I want to add cart items to wc_session data in order to be able to update cart data for a specific user.
I found the below code and used it, It gives me a 200 response, but no items in the cart.
What's the problem? Should I implement a specific webhook for this task, or the rest API will do?
Also, how can I add session data to meta tables directly from the code, as I am afraid to corrupt the database.
The code:
<?php
defined( 'ABSPATH' ) || exit;
class WC_REST_Webhooks_Controller extends WC_REST_Webhooks_V2_Controller {
protected $namespace = 'wc/v3';
protected function get_default_api_version() {
return 'wp_api_v3';
}
protected $rest_base = 'custom';
function woocommerce_add_to_cart($param) {
global $wpdb;
$user_id = $param['user_id'];
wp_set_current_user($user_id);
$objProduct = new WC_Session_Handler();
$wc_session_data = $objProduct->get_session($user_id);
$full_user_meta = get_user_meta($user_id, '_woocommerce_persistent_cart_1', true);
if( defined( 'WC_ABSPATH' ) ){
include_once WC_ABSPATH . 'includes/wc-cart-functions.php';
include_once WC_ABSPATH . 'includes/wc-notice-functions.php';
include_once WC_ABSPATH . 'includes/wc-template-hooks.php';
}
if ( null === WC()-> session ) {
$session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );
WC()->session = new $session_class();
WC()->session->init();
}
if ( null === WC()->customer ) {
WC()->customer = new WC_Customer( get_current_user_id(), true );
}
if ( null === WC()->cart ) {
WC()->cart = new WC_Cart();
// force refresh cart contents from the session here.
WC()->cart->get_cart();
}
// create a new cart object
$cartObj = WC()->cart;
// Add old cart data to newly created cart obect:
if ($full_user_meta['cart']) {
foreach($full_user_meta['cart'] as $single_user_meta) {
$cartObj->add_to_cart( $single_user_meta['product_id'], $single_user_meta['quantity'] );
}
}
//Add product and quantities coming in request to the new cart object
if ($param['products']) {
WC()->cart->empty_cart();
foreach($param['products'] as $prod) {
$cartObj->add_to_cart( $prod['product_id'], $prod['quantity']);
}
}
$updatedCart = [];
foreach( $cartObj->cart_contents as $key => $val ){
unset($val['data']);
$updatedCart[$key] = $val;
}
// if there is a current session cart, overwrite it with the new cart
if( $wc_session_data ){
$wc_session_data['cart'] = serialize($updatedCart);
$serializedObj = maybe_serialize($wc_session_data);
$table_name = 'wp_woocommerce_sessions';
// Update wp session table with cart data:
$sql = "UPDATE $table_name SET session_value= '".$serializedObj."' WHERE session_key = '".$user_id."'";
// Execute the query:
$rez = $wpdb->query($sql);
}
// Overwrite the persistent cart with new cart data
$full_user_meta['cart'] = $updatedCart;
$productsInCart = [];
foreach($cartObj->cart_contents as $cart_item) {
$product = wc_get_product( $cart_item['product_id'] );
$image_id = $product->get_image_id();
$image_url = wp_get_attachment_image_url( $image_id, 'full');
$productsInCart[] = (object) [
"product_id" => $cart_item['product_id'],
"product_name" => $product->get_name(),
"product_regular_price" => $product->get_regular_price(),
"product_sale_price" => $product->get_sale_price(),
"thumbnail" => $image_url,
"qty" => $cart_item['quantity'],
"line_subtotal" => $cart_item['line_subtotal'],
"line_total" => $cart_item['line_total'],
];
}
update_user_meta(get_current_user_id(), '_woocommerce_persistent_cart_1', array('cart' => updatedCart, ));
$response = [
'status' => true,
'data' => $full_user_meta['cart'] != null ? $productsInCart : []
];
return rest_ensure_response($response);
}
function woocommerce_cart_list($param) {
$user_id = $param['user_id'];
$objProduct = new WC_Session_Handler();
$wc_session_data = $objProduct-> get_session($user_id);
// get the persistent cart may be _woocommerce_persistent_cart can be in ur case check in user_meta table
$full_user_meta = get_user_meta($user_id, ' _woocommerce_persistent_cart_1 ', true);
$productsInCard = [];
foreach($full_user_meta['cart'] as $cart_item) {
$product = wc_get_product( $cart_item['product_id'] );
$image_id = $product->get_image_id();
$image_url = wp_get_attachment_image_url( $image_id, 'full');
$productsInCart[] = (object) [
"product_id" => $cart_item['product_id'],
"product_name" => $product->get_name(),
"product_regular_price" => $product->get_regular_price(),
"product_sale_price" => $product->get_sale_price(),
"thumbnail" => $image_url,
"qty" => $cart_item['quantity'],
"line_subtotal" => $cart_item['line_subtotal'],
"line_total" => $cart_item['line_total'],
];
}
$response = [
'status' => true,
'data' => $full_user_meta['cart'] != null ? $productsInCart : []
];
return rest_ensure_response($response);
}
public function register_routes() {
register_rest_route(
$this->namespace,
'/addtocart',
array(
'methods' => 'POST',
'callback' => array( $this, 'woocommerce_add_to_cart'),
)
);
register_rest_route(
$this->namespace,
'/cart',
array(
'methods' => 'GET' ,
'callback' => array(
$this, 'woocommerce_cart_list'
),
)
);
}
}```
I have a hash variable as a tree:
\%data = {
'node' => {
'RN:4' => {
'next' => {
'1' => {
'RN:23' => {
'next' => {
'1' => {
'RN:29' => {
'end' => 1
}
},
'2' => {
'RN:32' => {
'next' => {
'1' => {
'RN:30' => {
'end' = 1
}
}
}
}
}
}
I want to convert this tree to correct paths like this:
1, RN:4 >> RN:23 >> RN:29
2, RN:4 >> RN:23 >> RN:32 >> RN:30
I have tried some recursive code but alway get wrong path.
Help me please !
The data structure is wholly too complicated. Hashes are being used as arrays, and it would be easier if the id wasn't used as the key. It would be better if a node looked like this:
{
id => ...,
children => [ ... ]
}
The structure would become
[
{
id => 'RN:4',
children => [
{
id => 'RN:23',
children => [
{
id => 'RN:29',
children => []
},
{
id => 'RN:32',
children => [
{
id => 'RN:30',
children => []
}
]
}
]
}
]
}
]
You need the id of all ancestors so we pass a long a list of the ancestors as the parameters.
use 5.016;
sub print_paths {
my $i = 0;
my $helper = sub {
my $node = $_[-1];
my $children = $node->{children};
if (#$children) {
__SUB__->(#_, $_) for #$children;
} else {
say $i, ", ", join(" >> ", map { $_->{id} } #_);
}
};
$helper->(#_);
}
print_paths($_) for #$roots;
The above assumes the ends are the nodes with no children. If your ends can have children, you have a trie. Simply add end => 1 to the end nodes and use the following as the core of the visitor:
if (#$children) {
__SUB__->(#_, $_) for #$children;
}
if ($node->{end}) {
say $i, ", ", join(" >> ", map { $_->{id} } #_);
}
With your format, it's trickier (and more expensive).
$node->{id} is replaced with (keys(%$node))[0].
$node->{children} is replaced with $node->{$id}{next}.
$node->{end} is replaced with $node->{$id}{end}.
for my $child (#$children) is replaced with for (my $j=1; my $child = $children->{$j}; ++$j).
use 5.016;
sub print_paths {
my $i = 0;
my $helper = sub {
my $node = $_[-1];
my $id = (keys(%$node))[0];
my $children = $node->{$id}{next};
if ($children) {
for (my $j=1; my $child = $children->{$j}; ++$j) {
__SUB__->(#_, $child) for #$children;
}
}
if ($node->{$id}{end}) {
say $i, ", ", join(" >> ", map { (keys(%$node))[0] } #_);
}
};
$helper->(#_);
}
print_paths($data->{node});
I am having dictionary like this.
print Dumper($emp)
$VAR1 = {
'mike' => {
'country' => {
'US' => {
'pop' => 100
}
}
}
}
I want to append a new entry inside 'country' like this.
$VAR1 = {
'mike' => {
'country' => {
'US' => {
'pop' => 100
},
'Canada' => {
'pop' => 101
}
}
}
}
Right now I am building it like this
$emp -> {$name}{country} = getCountry();
sub getCountry{
....
return country;
}
It's not clear what getCountry returns. Seeing as it's a single scalar, I'm going to assume it's a hash of countries keyed by name despite the name.
{ Canada => { pop => 101 } }
A simple way to merge two hashes is
%h = ( %h, %new );
so
%{ $emp->{$name}{country} } = (
%{ $emp->{$name}{country} },
%{ getCountry() },
);
If getCountry were to return the country's name and the country, you'd could use the following:
my ($country_name, $country) = getCountry();
$emp->{$name}{country}{$country_name} = $country;
So, if the hash returned by getCountry returns just a single country, you could also do the following without changing getCountry:
my ($country_name, $country) = %{ getCountry() };
$emp->{$name}{country}{$country_name} = $country;
I have the following mongo shell code that I'm trying to use in my php application.
It's basically taking 800 evenly distributed values over a time frame, for use in a graph.
var map = function () {
if (endTime < 0) {
var start = new ISODate("2013-09-01T00:00:00.000Z");
var end = new ISODate("2013-11-01T00:00:00.000Z");
var startMilli = start.getTime();
var endMilli = end.getTime();
var interval = endMilli - startMilli;
delta = interval / 800;
endTime = startMilli + delta;
}
if (endTime < this.date.getTime()) {
resArray = {};
while (endTime < this.date.getTime()) {
endTime += delta;
}
}
var id = this.homeId + this.sensor;
if (typeof resArray[id] == 'undefined') {
resArray[id] = 1;
emit({
homeId: this.homeId,
sensor: this.sensor,
date: this.date,
val: this.val
}, {
x: 1
});
}
};
var reduce = function (key, values) {
return values[0];
};
db.passiv.mapReduce(
map,
reduce,
{ query:
{ homeId: 35600,
sensor : {
$in :[ "z1t","ts1"]
},
date : {
$gte : new ISODate("2013-09-01T00:00:00.000Z"),
$lte : new ISODate("2013-11-01T00:00:00.000Z")
} },
scope : {
resArray : {},
delta : -1,
endTime : -1
},
out: 'TEST1',
sort : {
date:1
}
});
It execute fine in the shell but my attempt to convert it to php is not giving me any results:
$from = '2013-09-01T00:00:00.000Z';
$to = '2013-11-01T00:00:00.000Z';
$map = new MongoCode("
var map = function () {
if (endTime < 0) {
var start = new ISODate(".$from.");
var end = new ISODate(".$to.");
var startMilli = start.getTime();
var endMilli = end.getTime();
var interval = endMilli - startMilli;
delta = interval / 800;
endTime = startMilli + delta;
}
if (endTime < this.date.getTime()) {
resArray = {};
while (endTime < this.date.getTime()) {
endTime += delta;
}
}
var id = this.homeId + this.sensor;
if (typeof resArray[id] == 'undefined') {
resArray[id] = 1;
emit({
homeId: this.homeId,
sensor: this.sensor,
date: this.date,
val: this.val},
{ x: 1 });
}
};");
$reduce = new MongoCode('
var reduce = function (key, values) {
return values[0];
};');
$constraint = array('homeId' => 32168);
$date = array('$gte' => new MongoDate(1377986400), '$lt' => new MongoDate(1383260400));
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => array('z1t', 'ts1'))));
$rs = $passivCollection->command(
array(
"mapreduce" => "passiv",
"query" => $condition,
"map" => $map,
"reduce" => $reduce,
"scope" => array(
"resArray" => array(),
"delta" => -1,
"endTime" => -1
),
"out" => "TEST1",
"sort" => array("date" => 1)
));
Somehow I think it's the scope in the command at the end that's not working properly and I cant seem to find anything useful on the scope aspect anywhere. Any help is greatly appreciated.
A good example of doing Map Reduce in PHP is located here: http://php.net/manual/en/mongodb.command.php at example #3.
Basically one of the problems you have is that the MongoCode objects do not represent anon functions anymore but instead a variable that cannot be returned.
Instead you want to define your functions like:
$reduce = new MongoCode('
function (key, values) {
return values[0];
};
');
That should fix it I think.
edit
There is a problem with your PHP in the your edit:
$start = new MongoDate(strtotime("2013-09-01 00:00:00"));
$end = new MongoDate(strtotime("2013-11-01 00:00:00"));
$constraint = array('homeId' => '32168');
$date = array('$gte' => $start, '$lt' => $end);
$sensors = array('z1t');
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => $sensors)));
var_dump($condition);
$tt = $passivCollection->selectCollection('passiv')->count($date);
var_dump($tt);
You are using:
$date = array('$gte' => $start, '$lt' => $end);
Without defining a field it should search on. Try:
$start = new MongoDate(strtotime("2013-09-01 00:00:00"));
$end = new MongoDate(strtotime("2013-11-01 00:00:00"));
$constraint = array('homeId' => '32168');
$date = array('date' => array('$gte' => $start, '$lt' => $end));
$sensors = array('z1t');
$condition = array_merge($constraint, $date, array("sensor" => array('$in' => $sensors)));
var_dump($condition);
$tt = $passivCollection->selectCollection('passiv')->count($date);
var_dump($tt);
Where the date string in:
$date = array('date' => array('$gte' => $start, '$lt' => $end));
is replaced by your field name.
I am trying to return today's birthdays. This is what I have right now, which works, but I need to grab the month and day to input into the statement. I thought maybe I could grab them from localtime, but that didn't work out. Any suggestions would be appreciated.
sub author_birth {
my ($self) = #_;
my ($day,$month) = (localtime())[3..4];
my $author_result = $self->search_like(
{
birth => '%03-20'
},
{
select => [
'id',
'complete_name',
],
#result_class => 'DBIx::Class::ResultClass::HashRefInflator'
}
);
my #author_ids = ();
while (my $row = $author_result->next) {
push #author_ids, $row->id;
}
return $self->get_author_info_by_id(\#author_ids);
}
I ended up doing something like this.
my ($self) = #_;
my $conc = '%';
my $datetime = Time::Piece->new->strftime('%m-%d');
my $date = $conc . $datetime;
my $author_result = $self->search_like(
{
birth => $date,
},
{
select => [
'id',
'complete_name',
],
#result_class => 'DBIx::Class::ResultClass::HashRefInflator'
}
);