Locale Swicher for multipages with render controller

I am trying to add language switcher and Locale was only working after page refresh.So I am planning to use Symfony Locale Swicher now.

I have two conroller.Along with that there is a languageController which calls an API and receices available lanuages: The result is an array of key value pairs.

Array ( [English] => en [Español] => es [Italiano] => it [Русский] => ru [日本語] => ja )

When I am using render controller , app.request.get(‘_route’) is always null. So I can’t dynamically cahnge the path.

li class="nav-item dropdown">
   <a class="nav-link dropdown-toggle" href="#" id="dropdown-language" role="button"
        data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
       English
    </a>
    <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown-language">
        <li><a class="dropdown-item" href="{{ path('index', {_locale: 'en'}) }}">English</a></li>
        <li><a class="dropdown-item" href="{{ path('index', {_locale: 'fr'}) }}">Français</a></li>
    </ul>
</li>

This is how I render languageController

return $this->render(
            'partials/header.html.twig',
            ['languageData' => $formattedLanguageData,]
        );

How can I modify the locale swicher so that I can access multipages(index,view) with my availabe language array.

Mocked protected method not returning value set in mock

I have a ModuleManifest class with this constructor:

/**
 * Create a new instance of the ModuleManifest class.
 *
 * @param string $vendor The vendor of the module.
 * @param string $package The name of the package.
 * @return void
 */
public function __construct(string $vendor, string $package)
{
    $manifest = $this->manifestPath($vendor, $package);

    if (!File::exists($manifest))
    {
        throw new InvalidArgumentException(sprintf(
            'Package %s from vendor %s does not contain a manifest',
            $vendor, $package
        ));
    }

    $this->manifest = $this->load($manifest);
}

This constructor loads a json file with the load method.

/**
 * Load a module manifest into an object.
 *
 * @param string $path The path to the module.
 * @return object
 */
protected function load(string $path) : object
{
    return json_decode(file_get_contents($path));
}

I want determine whether the method is returning an object:

/** @testdox Returns an object when the path exists */
public function test_manifest_returns_object() : void
{
    File::shouldReceive('exists')->once()->andReturnTrue();

    $this->mock(ModuleManifest::class, function (MockInterface $mock) {
        $mock->shouldAllowMockingProtectedMethods();
        $mock->shouldReceive('load')->once()->andReturn(
            json_decode('{"name": "testModule"}')
        );
    });

    $manifest = new ModuleManifest('vendor', 'package');

    $this->assertTrue($manifest->has('name'));
}

I get the error:

ErrorException: file_get_contents(…): Failed to open stream: No such file or directory

I would expect it to return the result of json_decode('{"name": "testModule"}'), but it tries to call the function normally.

Converting PDF to get a preview image using Imagick showing me the wrong colors

That’s my PHP code

$pdfPath = $_GET['path'];
$imagick = new Imagick();
$imagick->setResolution(30, 30);
$imagick->readImage($pdfPath);
$imagick->setImageFormat('png');
$imagick->setIteratorIndex(0);
header('Content-Type: image/png');
echo $imagick;
$imagick->clear();
$imagick->destroy();

and this is the result:

https://imgur.com/gnTJLEC

top = original

bottom = preview using imagick

I tried searching in StackOverflow and other places but the answers seems to be not PHP related and that’s beyond my knowledge. what should I do?

Laravel download and extract archives

Is there a way to shorten or make it easier to save and decompress an archive in laravel like below?

    public function upload(Request $request, string $slug)
    {
        $request->validate([
            "zipfile" => 'required'
        ]);

        $game = Game::where('slug', $slug)->first();

        if ($request->user()->id != $game->user_id) {
            return response([
                "status" => "invalid",
                "message" => "User is not author of the game"
            ], 400);
        }

        $version = Version::create([
            "game_id" => $game->id,
            "version" => now(),
            "path" => "/$game->slug/temp/index.html",
        ]);

        $dir_path = $game->slug . '/';
        $file = $request->file('zipfile');
        $zip = new ZipArchive();
        $file_new_path = $file->storeAs($dir_path . $version->id, 'zipname', 'public');
        $zipFile = $zip->open(Storage::disk('public')->path($file_new_path));
        if ($zipFile === TRUE) {
            $zip->extractTo(Storage::disk('public')->path($dir_path . $version->id));
            $zip->close();
        } else {
            return response([
                "status" => "invalid",
                "message" => "ZIP file extraction fails"
            ], 400);
        }
        $url = Storage::url($dir_path . $version->id);

        if (file_exists(Storage::disk('public')->path($dir_path . $version->id . "/thumbnail.png"))) {
            $game->thumbnail = url($url . "/thumbnail.png");
            $game->save();
        }
        $version->path = url($url . "/index.html");
        $version->save();
        return response($version);
    }

This code is quite working, but it looks very large, I would like to make it smaller

How to fetch data with get method from an api and loop throw it?

I make a function and use http:get in order to fetch from an api, but as it is associative array it is hard to reach end of element such as title, how can solve it?

<?php

namespace AppHttpControllers;
use IlluminateSupportFacadesHttp;
use IlluminateHttpRequest;

    class apiTest extends Controller
    {
        function data()
        {
            $get = http::get('https://jsonplaceholder.typicode.com/posts');
             $data = $get ->json();
                foreach ($data as $key => $datas) {
                    foreach ($datas as $key => $value){
                        echo $value;
                    }
                }
    
        }
     
    }

The problem of displaying likes and dislikes in comments

I put a like and dislike system for comments so that each comment can be liked and disliked, But there are 2 problems :

  1. For example, if I have 10 comments, the like and dislike buttons will display correctly in all of them, but if there is no like or dislike comment, it will show the value 0000000000 (10 to zero) instead of 0.
  2. When I click on the Like or Dislike button, the information is saved in wp_commentmeta table, but not correctly, In the Reza section, instead of registering the ID of the comment, it registers the ID of the post
    enter image description here
type here

This is my php codes :

function ldcPostLikeDislikeDetailTable() {
    global $wpdb;
    $db_table_name = $wpdb->prefix . 'like_dislike_btn_details';  // table name
    $charset_collate = $wpdb->get_charset_collate();
    $sql = "CREATE TABLE $db_table_name (
        id int(11) NOT NULL auto_increment,
        btn_container varchar(1000),
        likeDislikeType varchar(20),
        show_one_home varchar(5),
        on_pages varchar(5),
        on_product_page varchar(5),
        onshowShare varchar(5),
        UNIQUE KEY id (id)
        ) $charset_collate;";
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
    add_option( 'test_db_version', $test_db_version );
   // Adding default values
    $wpdb->insert( $db_table_name, array( 'btn_container' => '<div class="button-container-likes-dislike">
        <button class="btn-start-1" id="post-like-btn"><i class="fa fa-thumbs-up"></i><span>Like</span><b>224</b></button>
        <button class="btn-start-1" id="post-dislike-btn"><i class="fa fa-thumbs-down"></i><span>Dislike</span><b>28</b></button>
        <label class="switch-on-off" style="display: none;"><input type="checkbox" class="input-on-off"><span class="slider-on-off round"></span></label></div>', 'likeDislikeType' => 'cookie-check', 'show_one_home' => 'no', 'on_pages' =>'no', 'on_product_page' =>'yes', 'onshowShare' =>'yes' ) );
} add_action( 'init', 'ldcPostLikeDislikeDetailTable' );

function ldcPostLikeDislikeCounter() {
    if (isset($_POST['counter_type'], $_POST['comment_id'])) {
        $counter_type = sanitize_key($_POST['counter_type']);
        $comment_id = sanitize_key($_POST['comment_id']);
    }
    if ($counter_type == "post-like-btn") {
        //First check dislike exist in user meta
        ldcDislikeAlreadyExist($comment_id);
        $user_result_like = ldcUserLikeIncrement($counter_type, $comment_id);
        if ($user_result_like == "1") {
            //Like added successfuly in user meta
            $post_result_like = ldcPostLikeIncrement($comment_id);
            if ($post_result_like == "1") {
                echo esc_html( "1" );
            }
        } else {
            //Likst does not posted
            echo esc_html( $user_result_like );
        }
    } else {
        ldcLikeAlreadyExist($comment_id);
        $user_result_dislike = ldcUserDislikeIncrement($counter_type, $comment_id);
        if ($user_result_dislike == "1") {
            //Like added successfuly in user meta
            $post_result_dislike = ldcPostDislikeIncrement($comment_id);
            if ($post_result_dislike == "1") {
                echo esc_html( "1" );
            }
        } else {
            //Likst does not posted
            echo esc_html( $user_result_dislike );
        }
    }
    die();
} add_action('wp_ajax_nopriv_ldcPostLikeDislikeCounter', 'ldcPostLikeDislikeCounter'); add_action('wp_ajax_ldcPostLikeDislikeCounter', 'ldcPostLikeDislikeCounter');

function ldcAjaxPostLikeDislikeEnqueueScripts() {
    wp_enqueue_style( 'like', get_stylesheet_directory_uri() . '/assets/css/style.css' );
    wp_enqueue_script( 'like', get_stylesheet_directory_uri() . '/assets/js/logic.js', array( 'jquery' ), null, true );
    wp_localize_script('like', 'counterURL', array(
        'ajax_url' => admin_url('admin-ajax.php')
    ));
    wp_localize_script('like', 'cookieURL', array(
        'cookie_ajax_url' => admin_url('admin-ajax.php')
    ));
} add_action('wp_enqueue_scripts', 'ldcAjaxPostLikeDislikeEnqueueScripts');

function shortcode_like_dislike( $comment_text ) {
    global $post, $wpdb;
    $table_name = $wpdb->prefix . 'like_dislike_btn_details';
    $c =  ldcLikeDislikeSourceBtns();
    $comment_text = $comment_text . $c;
    return $comment_text;
} add_filter( 'comment_text', 'shortcode_like_dislike');

function ldcLikeDislikeSourceBtns(){
    #global $wp_query;
    #$postid = $wp_query->post->ID;
    $commentid = get_comment_ID();
    //Post meta
    #$post_like_counter = get_post_meta($postid, 'post_likes_plusCounter', true);
    $post_like_counter = get_comment_meta($commentid, 'post_likes_plusCounter', true);
    #$post_dislike_counter = get_post_meta($postid, 'post_dislikes_plusCounter', true);
    $post_dislike_counter = get_comment_meta($commentid, 'post_dislikes_plusCounter', true);
    if ($post_like_counter == "") {
        $post_like_counter = "0";
    }
    if ($post_dislike_counter == "") {
        $post_dislike_counter = "0";
    }
    global $wpdb;
    $table_name = $wpdb->prefix . 'like_dislike_btn_details';
    $button_db_details = $wpdb->get_row("SELECT * FROM $table_name WHERE id = '1' ");
    $c = "<div class='post-like-dislike-plusCounter-container'>".$button_db_details->btn_container."</div><span class='hidden-id' id='like-dislike-post-id'>".get_the_ID()."</span><span class='hidden-id ldc-post-total-likes'>".$post_like_counter."</span><span class='hidden-id ldc-post-total-dislikes'>".$post_dislike_counter."</span><span class='hidden-id ldc-lk-dk-type'>".$button_db_details->likeDislikeType."</span><span class='hidden-id ldc-page-title'>".get_the_title()."</span><span class='hidden-id ldc-share-it'>".$button_db_details->onshowShare."</span>";
    return $c;
}

function ldcUserLikeIncrement($counter_type, $comment_id) {
    $sendBack = "";
    if (is_user_logged_in()) {
        $user_id = get_current_user_id();
        $user_like_posts = get_user_meta($user_id, 'post_likes_plusCounter', true);
        if ($user_like_posts == "") {
            $comment_id = $comment_id . ",";
            add_user_meta($user_id, 'post_likes_plusCounter', $comment_id);
            // Like added successuflly
            $sendBack = "1";
        } else {
            $comma_seperate_post_ids = explode(',', $user_like_posts);
            if (in_array($comment_id, $comma_seperate_post_ids)) {
                //like aready exist of current user.
                $sendBack = "0";
            } else {
                //update user meta with new post id
                $user_like_posts = $user_like_posts . "" . $comment_id . ",";
                update_user_meta($user_id, 'post_likes_plusCounter', $user_like_posts);
                // Like added successuflly
                $sendBack = "1";
            }
        }
    } else {
        //User not login
        $sendBack = "2";
    }
    return $sendBack;
}

function ldcUserDislikeIncrement($counter_type, $comment_id) {
    $sendBack = "";
    if (is_user_logged_in()) {
        $user_id = get_current_user_id();
        $user_like_posts = get_user_meta($user_id, 'post_dislikes_plusCounter', true);
        if ($user_like_posts == "") {
            $comment_id = $comment_id . ",";
            add_user_meta($user_id, 'post_dislikes_plusCounter', $comment_id);
            // Like added successuflly
            $sendBack = "1";
        } else {
            $comma_seperate_post_ids = explode(',', $user_like_posts);
            if (in_array($comment_id, $comma_seperate_post_ids)) {
                //like aready exist of current user.
                $sendBack = "0";
            } else {
                //update user meta with new post id
                $user_like_posts = $user_like_posts . "" . $comment_id . ",";
                update_user_meta($user_id, 'post_dislikes_plusCounter', $user_like_posts);
                // Like added successuflly
                $sendBack = "1";
            }
        }
    } else {
        //User not login
        $sendBack = "2";
    }
    return $sendBack;
}

function ldcPostLikeIncrement($comment_id) {
    #$post_like_posts = get_post_meta($post_id, 'post_likes_plusCounter', true);
    $post_like_posts = get_comment_meta($comment_id, 'post_likes_plusCounter', true);
    if ($post_like_posts == "") {
        #add_post_meta($post_id, 'post_likes_plusCounter', '1');
        add_comment_meta($comment_id, 'post_likes_plusCounter', '1');
    } else {
        $post_like_posts = $post_like_posts + 1;
        #update_post_meta($post_id, 'post_likes_plusCounter', $post_like_posts);
        update_comment_meta($comment_id, 'post_likes_plusCounter', $post_like_posts);
    }
    return "1";
}

function ldcPostDislikeIncrement($comment_id) {
    #$post_like_posts = get_post_meta($post_id, 'post_dislikes_plusCounter', true);
    $post_like_posts = get_comment_meta($comment_id, 'post_dislikes_plusCounter', true);
    if ($post_like_posts == "") {
        #add_post_meta($post_id, 'post_dislikes_plusCounter', '1');
        add_comment_meta($comment_id, 'post_dislikes_plusCounter', '1');
    } else {
        $post_like_posts = $post_like_posts + 1;
        #update_post_meta($post_id, 'post_dislikes_plusCounter', $post_like_posts);
        update_comment_meta($comment_id, 'post_dislikes_plusCounter', $post_like_posts);
    }
    return "1";
}

function ldcDislikeAlreadyExist($comment_id) {
    $user_id = get_current_user_id();
    $user_like_posts = get_user_meta($user_id, 'post_dislikes_plusCounter', true);
    if ($user_like_posts == "") {
        return "Not exist";
    } else {
        $comma_seperate_post_ids = explode(',', $user_like_posts);
        if (in_array($comment_id, $comma_seperate_post_ids)) {
            //dislike aready exist of the current user.
            $key = array_search($comment_id, $comma_seperate_post_ids);
            if (false !== $key) {
                unset($comma_seperate_post_ids[$key]);
                $updated_counter = implode(',', $comma_seperate_post_ids);
                if($updated_counter==""){
                    $updated_counter = ",";
                }
                update_user_meta($user_id, 'post_dislikes_plusCounter', $updated_counter);
                ldcDislikeDecrease($comment_id);
            }
        }
    }
}

function ldcDislikeDecrease($comment_id){
    if (is_user_logged_in()) {
        #$user_dislike_posts = get_post_meta( $post_id, 'post_dislikes_plusCounter', true );
        $user_dislike_posts = get_comment_meta( $comment_id, 'post_dislikes_plusCounter', true );
        $user_dislike_posts = $user_dislike_posts - 1;
        #update_post_meta( $post_id, 'post_dislikes_plusCounter', $user_dislike_posts );
        update_comment_meta( $comment_id, 'post_dislikes_plusCounter', $user_dislike_posts );
        echo esc_html( "1" );
    }
}

function ldcLikeAlreadyExist($comment_id) {
    $user_id = get_current_user_id();
    $user_like_posts = get_user_meta($user_id, 'post_likes_plusCounter', true);
    if ($user_like_posts == "") {
        return "Not exist";
    } else {
        $comma_seperate_post_ids = explode(',', $user_like_posts);
        if (in_array($comment_id, $comma_seperate_post_ids)) {
            //dislike aready exist of the current user.
            $key = array_search($comment_id, $comma_seperate_post_ids);
            if (false !== $key) {
                unset($comma_seperate_post_ids[$key]);
                $updated_counter = implode(',', $comma_seperate_post_ids);
                if($updated_counter==""){
                    $updated_counter = ",";
                }
                update_user_meta($user_id, 'post_likes_plusCounter', $updated_counter);
                ldcLikeDecrease($comment_id);
            }
        }
    }
}

function ldcLikeDecrease($comment_id){
    if (is_user_logged_in()) {
        #$user_dislike_posts = get_post_meta( $post_id, 'post_likes_plusCounter', true );
        $user_dislike_posts = get_comment_meta( $comment_id, 'post_likes_plusCounter', true );
        $user_dislike_posts = $user_dislike_posts - 1;
        #update_post_meta( $post_id, 'post_likes_plusCounter', $user_dislike_posts );
        update_comment_meta( $comment_id, 'post_likes_plusCounter', $user_dislike_posts );
        echo esc_html( "1" );
    }
}

This is my javascript code :

  jQuery(document).ready(function( $ ) {
    // Show like and dislike buttons
    $('#post-like-btn b').text($('.ldc-post-total-likes').text()); //show text
    $('#post-dislike-btn b').text($('.ldc-post-total-dislikes').text()); //show text
    $('.post-like-dislike-plusCounter-container').css("display", "block"); //show buttons
  });

  var btnOpacityId = "";
  jQuery( document ).on( 'click', '.button-container-likes-dislike button', function() {
    btnOpacityId = jQuery(this).attr('id');
        jQuery(this).css("opacity", "0.4");
        var counter_type = jQuery(this).attr('id').trim();
        var post_id = jQuery("#like-dislike-post-id").text().trim();
        jQuery.ajax({
            url : counterURL.ajax_url,
            type : 'post',
            data : {
                action : 'ldcPostLikeDislikeCounter',
                counter_type : counter_type,
                post_id      : post_id
            },
            success : function( response ) {
                jQuery("#"+counter_type).css("opacity", "1");
                response = response.trim();
                console.log(response);
                if (counter_type == "post-like-btn") {
                    if (response=="1") {
                    //increment like
                    var inc = parseInt(jQuery('#post-like-btn b').text());
                    jQuery('#post-like-btn b').text(inc+1);
                    jQuery('.social-share-back').fadeIn();
                }
                else if (response=="11") {
                    //increment like
                    var inc = parseInt(jQuery('#post-like-btn b').text());
                    jQuery('#post-like-btn b').text(inc+1);
                    //dislike decrement
                    var desc = parseInt(jQuery('#post-dislike-btn b').text());
                    jQuery('#post-dislike-btn b').text(desc-1);
                    jQuery('.social-share-back').fadeIn();
                }
                else if(response == "0"){
                    alert("Like already exist for this post");
                }
                else if(response == "2"){
                    alert("Please, first you need to login if you want to vote!");
                }
            }
            else{
                if (response=="1") {
                    var inc = parseInt(jQuery('#post-dislike-btn b').text());
                    jQuery('#post-dislike-btn b').text(inc+1);
                }
                else if (response=="11") {
                    var inc = parseInt(jQuery('#post-dislike-btn b').text());
                    jQuery('#post-dislike-btn b').text(inc+1);
                    // Dislike decrement
                    var inc = parseInt(jQuery('#post-like-btn b').text());
                    jQuery('#post-like-btn b').text(inc-1);
                }
                else if(response == "0"){
                    alert("Dislike already exist for this post");
                }
                else if(response == "2"){
                    alert("Please, first you need to login if you want to vote!");
                }
            }
        }
    });
})

This is my css codes :

#likes-dislikes{
    display: flex;
    justify-content: space-around;
    flex:1;
    list-style: none;

}
#likes-dislikes :first-child span{
    background-color: #00D100;
    border: 2px solid #b0b7da;
    color: white;
    font-weight: 900;
    margin-top:5px;
    padding: 5px 10px;
    align-items: center;
    justify-content: center;
    box-shadow: inset 0 0 0 0 #fff;
    -webkit-transition: ease-out 0.4s;
    -moz-transition: ease-out 0.4s;
    transition: ease-out 0.4s;
    
}

#likes-dislikes :last-child span{
    background-color: #E22C05;
    border: 2px solid #b0b7da;
    color: white;
    font-weight: 900;
    margin-top:5px;
    padding: 5px 10px;
    align-items: center;
    justify-content: center;
    box-shadow: inset 0 0 0 0 #fff;
    -webkit-transition: ease-out 0.4s;
    -moz-transition: ease-out 0.4s;
    transition: ease-out 0.4s;
    
}

#likes-dislikes a{
    background-color: #372c3f;
    border: 2px solid #b0b7da;
    border-right: 0px;
    color: white;
    margin-top:5px;
    cursor: pointer;
    padding: 5px 30px;
    align-items: center;
    justify-content: center;
    box-shadow: inset 0 0 0 0 #fff;
    -webkit-transition: ease-out 0.4s;
    -moz-transition: ease-out 0.4s;
    transition: ease-out 0.4s;
    
}

#likes-dislikes :first-child a:hover  {
    border:2px solid #00D100;
    position: relative;
    box-shadow: inset 400px 0 0 0 #fff;
}

#likes-dislikes :first-child a:hover:after {
   content:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE4LjEuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMTU1LjEyMyAxNTUuMTIzIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxNTUuMTIzIDE1NS4xMjM7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxnPg0KCQk8cGF0aCBzdHlsZT0iZmlsbDojMDEwMDAyOyIgZD0iTTE1MC42NjksODQuMDY4YzcuODU4LTcuODIzLDUuNDMtMjMuNjQ3LTguMTgxLTIzLjY0N2wtMzUuODEzLDAuMDI0DQoJCQljMS4zNi03LjU4NCwzLjMzLTIwLjE1NiwzLjI1Mi0yMS4zNDNjLTAuNzUyLTExLjI0Mi03LjkxOC0yNC45MjQtOC4yMjgtMjUuNDg0Yy0xLjMwNy0yLjQzNC03LjkwNi01LjczNC0xNC41NDctNC4zMg0KCQkJYy04LjU4NiwxLjgzOC05LjQ2Myw3LjMxNS05LjQyOCw4LjgyNWMwLDAsMC4zNywxNC45ODMsMC40MDYsMTguOTgxYy00LjEwNSw5LjAxNi0xOC4yNTksMzIuNzEtMjIuNTQ5LDM0LjUzNg0KCQkJYy0xLjAyNi0wLjYyMS0yLjE5LTAuOTU1LTMuNDAxLTAuOTU1SDYuOTM0QzMuMDkxLDcwLjY4NSwwLDczLjc5MywwLDc3LjYxOGwwLjAwNiw2Mi41MzNjMC4yNjksMy4zNzEsMy4xMzMsNi4wMTUsNi41MTYsNi4wMTUNCgkJCWg0MC42NGMzLjYwNCwwLDYuNTM0LTIuOTMsNi41MzQtNi41MzR2LTIuMDc2YzAsMCwxLjUxLTAuMTEzLDIuMTk2LDAuMzI4YzIuNjEzLDEuNjU5LDUuODQyLDMuNzQ3LDEwLjA1NCwzLjc0N2g2MC42NDcNCgkJCWMyMi42NzQsMCwyMC4yNC0yMC4xMjYsMTguMTY5LTIyLjg3MWMzLjgzMS00LjE3MSw2LjItMTEuNTI4LDIuOTY2LTE3LjM0QzE1MC4yMSw5OC43ODksMTU0LjU3OCw5MS41NTcsMTUwLjY2OSw4NC4wNjh6DQoJCQkgTTQ1Ljc2NiwxMzkuNjJINi41MVY3Ny4yMTJoMzkuMjU2VjEzOS42MnogTTE0MC4wOSw4My41MzFsLTAuMzcsMS41NDVjMTAuNDQ4LDIuOTcxLDQuODg3LDE1LjAxMy0yLjYwOCwxNS43OTRsLTAuMzcsMS41NDUNCgkJCWMxMC4wMTgsMi41NDgsNS4yMzksMTQuOTQ3LTIuNjA4LDE1Ljc5NGwtMC4zNywxLjUzOWM4LjE4MSwxLjM0Myw2LjIsMTUuMzA1LTYuMTk0LDE1LjMwNWwtNjEuNjg2LDAuMDI0DQoJCQljLTQuMzU2LDAtOC4zMjQtNC45NjQtMTEuNTI4LTQuOTY0SDUxLjU2VjgyLjA3NWMzLjQ4NS0yLjE2LDcuNzY5LTQuOTY0LDEwLjE1LTYuOTg3YzQuNDk5LTMuODM3LDIyLjkxMy0zMy41OTMsMjIuOTEzLTM3LjMxNw0KCQkJcy0wLjQwNi0xOS44MzQtMC40MDYtMTkuODM0czMuNjEtNC42NTQsMTEuNjcxLTEuMjU5YzAsMCw2Ljc4NCwxMi43MjEsNy40NzYsMjIuODU5YzAsMC0zLjA1NSwyMC44ODQtNC42OTYsMjcuNDM2aDQyLjc2NQ0KCQkJQzE1MS45NCw2Ni45ODUsMTQ5LjkzNSw4MS45ODYsMTQwLjA5LDgzLjUzMXoiLz4NCgk8L2c+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8L3N2Zz4NCg==) ;
    display: block;
    position: absolute;
    width: 30%;
    height: 30%;
    margin: -26px 33px 0px 33px;;
    padding: 0;
    align-items: center;
    justify-content: center;
    transition: all .35s ease-Out;
    color:#fff;
}

#likes-dislikes :last-child a:hover {
    
    border:2px solid #E22C05;
    position: relative;
    box-shadow: inset 400px 0 0 0 #fff;
}
#likes-dislikes :last-child a:hover:after {
    content: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxnPg0KCQk8cGF0aCBkPSJNNTEyLDIwOGMwLTE4LjQ5Ni0xMC42MDMtMzQuNzMxLTI2LjM0Ny00Mi42NjdjMy4yODUtNi41NDksNS4wMTMtMTMuODAzLDUuMDEzLTIxLjMzMw0KCQkJYzAtMTguNTE3LTEwLjYwMy0zNC43NTItMjYuMzY4LTQyLjY4OGM0Ljg0My05LjcwNyw2LjI5My0yMC45MjgsMy44NC0zMi4wNDNDNDYzLjM4MSw0Ny42NTksNDQzLjA1MSwzMiw0MTkuODE5LDMySDIyNA0KCQkJYy0xNC44MDUsMC0zOC43Miw0Ljc3OS01Ni40NjksMTMuOTMxYy03LjQyNC0yMC41NDQtMjcuMTE1LTM1LjI2NC01MC4xOTctMzUuMjY0aC02NEMyMy45MzYsMTAuNjY3LDAsMzQuNjAzLDAsNjR2MTcwLjY2Nw0KCQkJQzAsMjY0LjA2NCwyMy45MzYsMjg4LDUzLjMzMywyODhIMTYwYzUuODg4LDAsMTAuNjY3LTQuNzc5LDEwLjY2Ny0xMC42Njd2LTQuMDc1bDIuNTYsMS4yOGw2MS40NCwxMzMuMTQxVjQ4MA0KCQkJYzAsMy4yNDMsMS40NzIsNi4yOTMsMy45ODksOC4zNDFjMC42ODMsMC41MzMsMTYuNTEyLDEyLjk5MiwzOC42NzcsMTIuOTkyYzI0LjY4MywwLDY0LTM5LjA2MSw2NC04NS4zMzMNCgkJCWMwLTI5LjE4NC0xMC40NTMtNjUuNTE1LTE2Ljk2LTg1LjMzM2gxMzEuNzU1YzI4LjcxNSwwLDUzLjE0MS0yMS4yMjcsNTUuNjM3LTQ4LjM0MWMxLjQwOC0xNS4xODktMy42NjktMjkuODI0LTEzLjYzMi00MC43MjUNCgkJCUM1MDYuOTAxLDIzMi43NjgsNTEyLDIyMC44MjEsNTEyLDIwOHogTTE0OS4zMzMsMjU2YzAsMC4wMjEsMCwwLjAyMSwwLDAuMDQzYzAsMC4wMjEsMCwwLjAyMSwwLDAuMDQzdjEwLjY2N2gtOTYNCgkJCWMtMTcuNjQzLDAtMzItMTQuMzU3LTMyLTMyVjY0LjA4NWMwLTE3LjY0MywxNC4zNTctMzIsMzItMzJoNjRjMTcuNjQzLDAsMzIsMTQuMzU3LDMyLDMyVjI1NnogTTQ3NS4zNDksMjUwLjE3Ng0KCQkJYzEwLjQ3NSw2LjQ2NCwxNi4yOTksMTguMDI3LDE1LjE2OCwzMC4yMDhjLTEuNDkzLDE2LjIzNS0xNi41OTcsMjguOTQ5LTM0LjM4OSwyOC45NDlIMzA5LjMzM2MtMy41MiwwLTYuNzYzLDEuNzQ5LTguNzY4LDQuNjI5DQoJCQljLTIuMDA1LDIuODgtMi40NTMsNi41NzEtMS4xOTUsOS44MzVDMjk5LjU2MywzMjQuMzMxLDMyMCwzNzguNDc1LDMyMCw0MTZjMCwzNC45MjMtMzAuOTc2LDY0LTQyLjY2Nyw2NA0KCQkJYy05LjE5NSwwLTE2LjkxNy0zLjM0OS0yMS4zMzMtNS44MDNoLTAuMDIxdi02OC43NzljMC0xLjUzNi0wLjM0MS0zLjA3Mi0wLjk4MS00LjQ1OWwtNjQtMTM4LjY2Nw0KCQkJYy0xLjAwMy0yLjE5Ny0yLjczMS0zLjk4OS00LjkwNy01LjA3N2wtMTUuNDI0LTcuNzIzVjY5LjE0MWMxMS45NDctOC44OTYsMzcuNDgzLTE1LjcyMyw1My4zMzMtMTUuNzIzaDE5NS44NjENCgkJCWMxMy4zMTIsMCwyNC44NzUsOC42MTksMjcuNDk5LDIwLjUyM2MyLjExMiw5LjUzNi0wLjcwNCwxOC44MTYtNy43NDQsMjUuNDcyYy0zLjAyOSwyLjgzNy00LjExNyw3LjE2OC0yLjc5NSwxMS4xMTUNCgkJCWMxLjMwMSwzLjkyNSw0Ljc3OSw2Ljc0MSw4Ljg5Niw3LjIxMWMxMy40NjEsMS41MzYsMjMuNjM3LDEyLjg0MywyMy42MzcsMjYuMzA0YzAsNy4zMzktMi45ODcsMTQuMjA4LTguNDI3LDE5LjM0OQ0KCQkJYy0zLjAyOSwyLjgzNy00LjExNyw3LjE2OC0yLjc5NSwxMS4xMTVjMS4zMDEsMy45MjUsNC43NzksNi43NDEsOC44OTYsNy4yMTFjMTMuNDYxLDEuNTM2LDIzLjYzNywxMi44NDMsMjMuNjM3LDI2LjMwNA0KCQkJYzAsOS45Mi01LjU4OSwxOC45NjUtMTQuNTkyLDIzLjU5NWMtMy40MTMsMS43NDktNS42MzIsNS4yMjctNS43ODEsOS4wNjdDNDcwLjEyMywyNDQuNTIzLDQ3Mi4wNjQsMjQ4LjE0OSw0NzUuMzQ5LDI1MC4xNzZ6Ii8+DQoJPC9nPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=);
    display: block;
    position: absolute;
    width: 30%;
    height: 30%;
    margin: -29px 33px 0px 33px;;
    padding: 2px;
    align-items: center;
    justify-content: center;
    transition: all .35s ease-Out;
    color:#fff;

}

My knowledge about programming is very little, I have been dealing with this issue for a few days, I am really confused, friends, if you can help me, please do not hesitate.

HasOneThrough Relationship on different connections

I have these models

class Device extends Model
{
    use HasFactory;

    protected $connection = 'traccar';
    
   public function vehicle()
   {
       //Code I need
   }
}

class Vehicle extends Model
{
    use HasFactory;

    protected $connection = 'mysql';
  
}

class VehicleDevice extends Model
{
    use HasFactory;
    protected $connection = 'mysql';
}

as you can see the Device is in a tracar database and the reset of the models are in the MySQL connection.

How do I write a hasOneThrough relationship for the devices to the vehicle?
I am getting an error that says

Table 'tracar.vehicles' doesn't exist (Connection: traccar, SQL: select * from `tc_devices` where exists (select * from `vehicles` inner join `vehicle_devices` on `vehicle_devices`.`id` = `vehicles`.`vehicle_device_id` where `tc_devices`.`id` = `vehicle_devices`.`device_id`))'

How to count each users records in database table and display count in view file

I want to get job count for each user but i am getting the error Using $this when not in object context (View: C:xampphtdocsproject104resourcesviewsusersshow.blade.php). I will appreciate any help, Thanks in advance.

User model function

 public function jobs()
        {
            return $this->hasMany(AppJob::class);
        }

        public static function getJobsCountAttribute(){
            return $this->jobs()->count();
        }

Job model

  public function users()
    {
        return $this->belongsTo(AppUser::class);
    }

In view file

   @php
    $getJobsCountAttribute = AppUser::getJobsCountAttribute();
    @endphp
    {{ $getJobsCountAttribute }}

Get shortcode attribute form wordpress post content

I Have a shortcode in post content:
[restrict paid="true"][download id='1234'][/restrict]

I need to extract 1234 value from the shortcode.
I use this funtion but it not work:

function get_download_ids( $content, $shortcode ) {
    $pattern = get_shortcode_regex();
    $ids = array();

    // Find download shortcodes.
    if (   preg_match_all( '/'. $pattern .'/s', $content, $matches ) &&
           array_key_exists( 2, $matches ) &&
           in_array( $shortcode, $matches[2] ) )
    {
        foreach( $matches[2] as $shortcode_index => $shortcode_name ) {
            if ( $shortcode_name === $shortcode ) {
                $shortcode_str = $matches[0][ $shortcode_index ];

                // Extract shortcode attributes.
                $shortcode_attrs_str = str_replace( $shortcode, '', $shortcode_str );
                $shortcode_attrs_str = trim( $shortcode_attrs_str, '[]' );

                // Convert attributes into array.
                $atts = shortcode_parse_atts( $shortcode_attrs_str );

                if ( ! empty( $atts['id'] ) ) {
                    $ids[] = $atts['id'];
                }
            }
        }
    }

    return array_unique( $ids );
}

Look forward to the help of everyone.
Thank you

How can I get clicked keyboard data from inline keyboard and answercallBackQuery or answerInlineQuery in laravel telegram-bot-sdk?

I setwebhook in there!
enter image description here

web.php

Route::post('/webhook', function () {
   $update = Telegram::commandsHandler(true);
});

StartCommand command:

  public function handle()
         {
             $text = 'Assalomu alaykum botimizga xush kelibsiz!'.chr(10).chr(10);
             $text .= 'Bugungi taomni baholang!'.chr(10).chr(10);
     
             $update = Telegram::getWebhookUpdate();
             $chat_id = $update->getMessage()->getChat()->getId();
     
             $keyboard = array(
     array(
            array('text'=>'1','callback_data'=>"1"),
            array('text'=>'2','callback_data'=>"2"),
            array('text'=>'3','callback_data'=>"3"),
            array('text'=>'4','callback_data'=>"4"),
            array('text'=>'5','callback_data'=>"5"),
                 )
             );
     
             $reply_markup = Keyboard::inlineButton([
                 'inline_keyboard' => $keyboard,
                 'resize_keyboard' => true,
                 'one_time_keyboard' => true,
                'hideInlineKeyboard' =>false,
             ]);
    
     Telegram::sendMessage([
                 'chat_id' => $chat_id,
                'text' => $text,
                'reply_markup' => $reply_markup
             ]);
        }

If the user clicks 1 or other numbers, the bot answers you clicked 1,… .`

show two dropdown selection in same form using php and javascript

please i need help for my problem which is view two dropdown selection in the same form …i trying in my code to show them all but it show only one dropdown select and when i click on the first dropdown select after click it it will show the second dropdown select …is there any solution for this problem ??
`

> here is my code
here is my code:
<head>
<script>
function showselect() {
$("#ff").submit();
}
$(document).ready(function()
{
$("#f").hide();

});
</script>
</head>
<body>
<form action="" method="POST" name="ff" id="ff">
<select id="" name="appname" class="s" >
<option value="" selected="true" disabled="disabled">date of appointment</option>
<?php
$query1 ="SELECT * FROM appo where p>0";
$result1 = $conn->query($query1);
if($result1->num_rows> 0){
while($option=$result1->fetch_assoc()){
$wed =$option['wed'];
$p =$option['p'];
?>
<option value="<?php echo $p; ?>"  ><?php echo $wed; ?> </option>
<?php
}}
if($_POST['appname']){
$appname=$_POST['appname'];
?>
<script>
$(document).ready(function()
{
$("#f").show();
});
</script>
<?php
$sqh="SELECT * FROM form where i ='$appname'";
?>
</select>
<?php
$sqh2="SELECT * FROM appo where p ='$appname'";
$querys = $conn->query($sqh2);
while($row3=mysqli_fetch_array($querys,MYSQLI_ASSOC)){
$p=$row3['p'];
$wed=$row3['wed'];?>
<div class="ff" id="f">date of appointment   :<?php echo $wed;?></div>
<?php
}
?>
<center>
<table class="table1" >
<?php 
echo"<tr>";
?>
<th style="width:30px;">checking</th><th style="width:250px;">full name of employee</th><th style="width:200px;">careertitle</th><th style="width:250px;">work place</th>
<th style="width:200px;">type of require</th>
<th style="width:250px;">date of require</th>
<?php
echo"</tr>";
$query = $conn->query($sqh);
while($row2=mysqli_fetch_array($query,MYSQLI_ASSOC)){
$id=$row2['id'];
$full=$row2['fullname'];
$career=$row2['careertitle'];
$work=$row2['workplace'];
$statee=$row2['statee'];
$datee=$row2['datee'];
echo"<tr>";
?><td ><input type="checkbox" name="chck[]" id="check" onclick="showbutton()" value=<?php echo $id ?>/> </td>
<?php
echo"<td>$full</td>";
echo"<td>$career</td>";
echo"<td>$work</td>";
echo"<td>$datee</td>";
$sql4="SELECT * FROM cstatee where empid='$statee'";
$query4=mysqli_query($conn,$sql4); 
while($row1=mysqli_fetch_array($query4,MYSQLI_ASSOC)){
$sname=$row1['sname'];
echo"<td>$sname</td>";
}
echo"</tr>";
}
}?>
</table>


<!--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////-->  

<select id="" name="tname" class="s">
<option value="" selected="true" disabled="disabled">the full requirement</option>
<?php 
$query ="SELECT * FROM cstatee";
$result = $conn->query($query);
if($result->num_rows> 0){
while($option=$result->fetch_assoc()){
$options =$option['sname'];
$id =$option['empid'];
?>
<option value="<?php echo $id; ?>"  ><?php echo $options; ?> </option>
<?php
}}
if($_POST['tname']){
$tname=$_POST['tname'];
?>
<script>
$(document).ready(function()
{
$("#f").show();
});
</script>
<?php
$sqh="SELECT * FROM form where statee ='$tname'";
?>
</select>
<?php
$sqh2="SELECT * FROM cstatee where empid ='$tname'";
$querys = $conn->query($sqh2);
while($row3=mysqli_fetch_array($querys,MYSQLI_ASSOC)){
$empid=$row3['empid'];
$sname=$row3['sname'];?>
<div class="ff" id="f">type of require :<?php echo $sname;?></div>
<?php
}
?>
<center>
<table class="table1" >
<?php 
echo"<tr>";
?>
<th style="width:30px;">checking </th><th style="width:250px;">full name of employee</th><th style="width:200px;">careertitle</th><th style="width:250px;">work place</th>
<th style="width:250px;">date of require</th>
<?php
echo"</tr>";
$query = $conn->query($sqh);
while($row2=mysqli_fetch_array($query,MYSQLI_ASSOC)){
$id=$row2['id'];
$full=$row2['fullname'];
$career=$row2['careertitle'];
$work=$row2['workplace'];
$datee=$row2['datee'];
echo"<tr>";
?><td ><input type="checkbox" name="chck[]" id="check" onclick="showbutton()" value=<?php echo $id ?>/> </td>
<?php
echo"<td>$full</td>";
echo"<td>$career</td>";
echo"<td>$work</td>";
echo"<td>$datee</td>";
echo"</tr>";
}
?>

<script>
$(document).ready(function(){
$("#update").slideToggle(1000);
});
</script>
</table>
<?php
}

?>



<input type="submit" name="update" id="update" value="editing" onClick="fup()"   style="width:250px;height:80px;color:black;
background-image: linear-gradient(to bottom right,rgb(211, 106, 134),#3cb59d);background-repeat: no-repeat;margin-top:20px;margin-bottom:20px;">

<?php
if(isset($_POST['update'])){
if(isset($_POST['chck'])){
foreach($_POST['chck'] AS $selected){
$_SESSION['id']=$selected;?>
<script>
window.location.href="update.php";
</script>
<?php
}
}    
}
?>
</form>
</body>````

Why the request work in Postman but no in my php file

thank you for this helpfull society.
I have an API that require a Token that’s taken based on my IP address. So, I tried to send a post request to the API through Postman and everything works fine, but when I try to send the request through a php file by my localhost, it gives me 500 statuc code – internal server error.

this is my code

require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl(Some Api URL);
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'some token key' => 'some token value based on my IP address'
));
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

I expect to get a status of 200 not 500 and internal server error

Hashing an image “expensive”?

I’m considering only allowing unique images to save storage space, and, to do this I will:

  1. Hash image on upload
  2. Check a database of images, to see if there is a hash match.

I have concerns though of hashing uploaded images slowing down the application.

Is this a recommended practice? Or do people tend to allow duplicate image uploads as storage is cheap?

Google Analytics Data API v1 (impressions) data does not match with GA4 Analytics / Ads data

I want to retrieve Google Ads related data (impressions, clicks, cost) from a GA4 Google Analytics property (linked to my Google Ads account) using the new Google Analytics Data API v1. My code works, but it returns very different numbers compared to what I see in Google Analytics or Google Ads. For example for the week of 15-21 May:

My code using GA Data API v1 Google Analytics Google Ads
Impressions 2472 ? 4.17K
Clicks 324 339 353
Cost 92.3 95.7 98.23

As you can see there is a very big difference especially for the number of impressions. (Unfortunately I could not find a way to check this number in Analytics for a large number of campaigns as “Exploration reports” only work for 10 campaigns or so.) I see similar differences for all other weeks.

Please see below my php code retrieving the data from Analytics. What am I doing wrong that I’m not getting the same (impression) numbers as in Google Analytics/Ads?

public function handle()
{

    $downloader = new GNGGA4DataDownloader();
    $results = $downloader->run('2023-05-15', '2023-05-21', config('services.google-analytics.propertyID'));

    $ti = 0;
    $tcl = 0;
    $tco = 0; 

    foreach ($results as $r) {
        $ti += $r->impressions;
        $tcl += $r->clicks;
        $tco += $r->cost;
    }

    dd($results, 'impressions: ' . $ti, 'clicks: ' . $tcl, 'cost: ' . $tco);

}

<?php
namespace AppGNG;

use GoogleAnalyticsDataV1alphaOrderByDimensionOrderBy;
use GoogleAnalyticsDataV1betaBetaAnalyticsDataClient;
use GoogleAnalyticsDataV1betaDateRange;
use GoogleAnalyticsDataV1betaDimension;
use GoogleAnalyticsDataV1betaMetric;
use GoogleAnalyticsDataV1betaOrderBy;
use stdClass;

//This is the current API to download Analytics data from GA4 properties.
class GNGGA4DataDownloader 
{

    private function downloadData($startDate, $endDate, $ga_propertyID) {

        $path = storage_path('app/GA/GNG GA4 API-XXX.json');
        putenv("GOOGLE_APPLICATION_CREDENTIALS=" . $path);

        $client = new BetaAnalyticsDataClient();

        // Make an API call.
        $response = $client->runReport([
            'property' => 'properties/' . $ga_propertyID,
            'dateRanges' => [
                new DateRange([
                    'start_date' => $startDate,
                    'end_date' => $endDate,
                ]),
            ],
            'dimensions' => [
                new Dimension(['name' => 'sessionCampaignName']),
                //new Dimension(['name' => 'sessionGoogleAdsCampaignName']),
                
                new Dimension(['name' => 'year']),
                new Dimension(['name' => 'isoWeek']),
            ],
            'metrics' => [
                new Metric(['name' => 'advertiserAdImpressions']), 
                new Metric(['name' => 'advertiserAdClicks']), 
                new Metric(['name' => 'advertiserAdCost']), 
                
            ],
            'orderBys' => [
                new OrderBy([
                    'dimension' => new OrderByDimensionOrderBy([
                        'dimension_name' => 'sessionCampaignName', 
                        //'dimension_name' => 'sessionGoogleAdsCampaignName', 
                        'order_type' => OrderByDimensionOrderByOrderType::ALPHANUMERIC
                    ]),
                    'desc' => false,
                ]),
            ],
            'keepEmptyRows' => true,  
            
        ]);

        return $response;


    }

    private function extractResults($response) {

        $results = array();

        foreach ($response->getRows() as $row) {

            $d = new stdClass();

            $d->campaignCode = $row->getDimensionValues()[0]->getValue();
            $d->isoYear = $row->getDimensionValues()[1]->getValue();
            $d->isoWeek = $row->getDimensionValues()[2]->getValue();
            $d->impressions = $row->getMetricValues()[0]->getValue();
            $d->clicks = $row->getMetricValues()[1]->getValue();
            $d->cost = $row->getMetricValues()[2]->getValue();
         
            array_push($results, $d);

        }

        return $results;

    }
    
    function run($startDate, $endDate, $gaPropertyID) {

        $response = $this->downloadData($startDate, $endDate, $gaPropertyID);

        $results = $this->extractResults($response);

        return $results;

    }

}