Is there a way to specify the database using the LDAP php extension to connect to an Active Directory?

Let’s say I have a server called examplesite.database.windows.net that has a database called Database. The username and password are [email protected] and password respectively.

I need to connect via “Active Directory – Password” authentication as per my company’s policy (I do not have privileges to change anything about the server, switching to alternative forms of authentication are also disallowed).

I would like to use PHP to connect to database and am currently trying out the LDAP php extension, but I could not find anything within the LDAP documentation that would allow me to specify the database property (“Database” in this example) to connect to. The account in question only has privileges for this database and not the entire server.

Can anyone point me in the right direction?

If LDAP does not enable the Database property to be specified, are there any alternatives within php that are suitable for Active Directory connections?

Some sample code I tried is below:

<?php 
$ldap_dn = "uid=account,dc=example,dc=com";
$ldap_password = "password";
$ldap_con = ldap_connect("examplesite.database.windows.net") or die('Could not connect to LDAP server'); 
ldap_set_option($ldap_con, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_con, LDAP_OPT_REFERRALS, 0);

if ($ldap_con)
    $ldap_bind = ldap_bind($ldap_con, $ldap_dn, $ldap_password);

    if ($ldap_bind) {
        echo "Bind Successful";
    } else {
        echo "Error";
}
?> 

GA4 Measurement Protocol purchase event not appearing in DebugView when sent from backend using stored _ga client_id

I’m trying to send a purchase event to Google Analytics 4 using the Measurement Protocol from my Laravel backend. I’m not using gtag.js or Firebase — only server-side requests.

Here’s what I’m doing:

  1. On the frontend, I extract the client_id from the _ga cookie using JavaScript and send it to the backend (e.g., via Ajax or form data).
  2. I store that client_id in the database (associated with the user or session).
  3. Later, when the user completes a purchase (which could be minutes or even days after the initial visit), I use the previously saved client_id to send a purchase event via Measurement Protocol.

Here’s a simplified version of the code I use:

public function gtmPaidOrder() {
    $clientId = $this->client_id; // previously saved from frontend _ga cookie

    if (!$clientId) return false;

    $measurement_id = 'G-XXXXXXX';
    $api_secret = MY_SECRET;

    $payload = [
        'client_id' => $clientId,
        'events' => [
            [
                'name'   => 'purchase',
                'params' => [
                    'transaction_id' => $this->id,
                    'value'          => $this->getTotalPrice(),
                    'currency'       => $this->currency,
                    'items'          => [
                        [
                            'item_id' => '123',
                            'item_name' => 'Test Product',
                            'price' => 20,
                            'quantity' => 1
                        ]
                    ],
                    'debug_mode' => true
                ]
            ]
        ]
    ];

    $url = "https://www.google-analytics.com/debug/mp/collect?measurement_id={$measurement_id}&api_secret={$api_secret}";

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    Log::info('GA4 MP response', ['response' => $response]);
}

Google returns a 200 OK response with the following body:

{
  "validationMessages": [ ]
}

All other events sent from the frontend appear in DebugView, but this event does not. Why?

Custom 404 page – Custom PHP framework

I’m using custom PHP framework (here) and when route not found it’s showing an error page:

Fatal error
Uncaught exception: 'Exception'

Message: 'No route matched.'

Stack trace:

#0 C:xampphtdocsprojectswashpublicindex.php(40): CoreRouter->dispatch('plan/plan/views')
#1 {main}
Thrown in 'C:xampphtdocsprojectswashCoreRouter.php' on line 130

This is very useful while programming but now I’ll go live and I need only 404 page appear if no route found.

I found 404 & 500 html page under App->Views directory but it’s not work!

Below I’ll put the code of index.php, error.php & routre.php files, and you can refer to the link above to check the whole framework structure.

Please note that sometimes I don’t know how to describe my issue so please notify me before you vote my question.

public/index.php:

<?php

/**
 * Front controller
 *
 * PHP version 7.0
 */

/**
 * Composer
 */
require dirname(__DIR__) . '/vendor/autoload.php';


/**
 * Error and Exception handling
 */
error_reporting(E_ALL);
set_error_handler('CoreError::errorHandler');
set_exception_handler('CoreError::exceptionHandler');

/**
 * Sessions
 */
 session_start();


/**
 * Routing
 */
$router = new CoreRouter();

// Add the routes
$router->add('', ['controller' => 'Home', 'action' => 'index']);
$router->add('Login', ['controller' => 'login', 'action' => 'new']);
$router->add('Logout', ['controller' => 'login', 'action' => 'destroy']);
$router->add('password/reset/{token:[da-f]+}', ['controller' => 'Password', 'action' => 'reset']);
$router->add('{controller}/{action}');
    
$router->dispatch($_SERVER['QUERY_STRING']);

Core>Error.php

<?php

namespace Core;

/**
 * Error and exception handler
 *
 * PHP version 7.0
 */
class Error
{

    /**
     * Error handler. Convert all errors to Exceptions by throwing an ErrorException.
     *
     * @param int $level  Error level
     * @param string $message  Error message
     * @param string $file  Filename the error was raised in
     * @param int $line  Line number in the file
     *
     * @return void
     */
    public static function errorHandler($level, $message, $file, $line)
    {
        if (error_reporting() !== 0) {  // to keep the @ operator working
            throw new ErrorException($message, 0, $level, $file, $line);
        }
    }

    /**
     * Exception handler.
     *
     * @param Exception $exception  The exception
     *
     * @return void
     */
    public static function exceptionHandler($exception)
    {
        // Code is 404 (not found) or 500 (general error)
        $code = $exception->getCode();
        if ($code != 404) {
            $code = 500;
        }
        http_response_code($code);

        if (AppConfig::SHOW_ERRORS) {
            echo "<h1>Fatal error</h1>";
            echo "<p>Uncaught exception: '" . get_class($exception) . "'</p>";
            echo "<p>Message: '" . $exception->getMessage() . "'</p>";
            echo "<p>Stack trace:<pre>" . $exception->getTraceAsString() . "</pre></p>";
            echo "<p>Thrown in '" . $exception->getFile() . "' on line " . $exception->getLine() . "</p>";
        } else {
            $log = dirname(__DIR__) . '/logs/' . date('Y-m-d') . '.txt';
            ini_set('error_log', $log);

            $message = "Uncaught exception: '" . get_class($exception) . "'";
            $message .= " with message '" . $exception->getMessage() . "'";
            $message .= "nStack trace: " . $exception->getTraceAsString();
            $message .= "nThrown in '" . $exception->getFile() . "' on line " . $exception->getLine();

            error_log($message);

            View::renderTemplate("$code.html");
        }
    }
}

Core>Router.php

<?php

namespace Core;

/**
 * Router
 *
 * PHP version 7.0
 */
class Router
{

    /**
     * Associative array of routes (the routing table)
     * @var array
     */
    protected $routes = [];

    /**
     * Parameters from the matched route
     * @var array
     */
    protected $params = [];

    /**
     * Add a route to the routing table
     *
     * @param string $route  The route URL
     * @param array  $params Parameters (controller, action, etc.)
     *
     * @return void
     */
    public function add($route, $params = [])
    {
        // Convert the route to a regular expression: escape forward slashes
        $route = preg_replace('///', '\/', $route);

        // Convert variables e.g. {controller}
        $route = preg_replace('/{([a-z]+)}/', '(?P<1>[a-z-]+)', $route);

        // Convert variables with custom regular expressions e.g. {id:d+}
        $route = preg_replace('/{([a-z]+):([^}]+)}/', '(?P<1>2)', $route);

        // Add start and end delimiters, and case insensitive flag
        $route = '/^' . $route . '$/i';

        $this->routes[$route] = $params;
    }

    /**
     * Get all the routes from the routing table
     *
     * @return array
     */
    public function getRoutes()
    {
        return $this->routes;
    }

    /**
     * Match the route to the routes in the routing table, setting the $params
     * property if a route is found.
     *
     * @param string $url The route URL
     *
     * @return boolean  true if a match found, false otherwise
     */
    public function match($url)
    {
        foreach ($this->routes as $route => $params) {
            if (preg_match($route, $url, $matches)) {
                // Get named capture group values
                foreach ($matches as $key => $match) {
                    if (is_string($key)) {
                        $params[$key] = $match;
                    }
                }

                $this->params = $params;
                return true;
            }
        }

        return false;
    }

    /**
     * Get the currently matched parameters
     *
     * @return array
     */
    public function getParams()
    {
        return $this->params;
    }

    /**
     * Dispatch the route, creating the controller object and running the
     * action method
     *
     * @param string $url The route URL
     *
     * @return void
     */
    public function dispatch($url)
    {
        $url = $this->removeQueryStringVariables($url);

        if ($this->match($url)) {
            $controller = $this->params['controller'];
            $controller = $this->convertToStudlyCaps($controller);
            $controller = $this->getNamespace() . $controller;

            if (class_exists($controller)) {
                $controller_object = new $controller($this->params);

                $action = $this->params['action'];
                $action = $this->convertToCamelCase($action);

                if (preg_match('/action$/i', $action) == 0) {
                    $controller_object->$action();

                } else {
                    throw new Exception("Method $action in controller $controller cannot be called directly - remove the Action suffix to call this method");
                }
            } else {
                throw new Exception("Controller class $controller not found");
            }
        } else {
            throw new Exception('No route matched.', 404);
        }
    }

    /**
     * Convert the string with hyphens to StudlyCaps,
     * e.g. post-authors => PostAuthors
     *
     * @param string $string The string to convert
     *
     * @return string
     */
    protected function convertToStudlyCaps($string)
    {
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $string)));
    }

    /**
     * Convert the string with hyphens to camelCase,
     * e.g. add-new => addNew
     *
     * @param string $string The string to convert
     *
     * @return string
     */
    protected function convertToCamelCase($string)
    {
        return lcfirst($this->convertToStudlyCaps($string));
    }

    /**
     * Remove the query string variables from the URL (if any). As the full
     * query string is used for the route, any variables at the end will need
     * to be removed before the route is matched to the routing table. For
     * example:
     *
     *   URL                           $_SERVER['QUERY_STRING']  Route
     *   -------------------------------------------------------------------
     *   localhost                     ''                        ''
     *   localhost/?                   ''                        ''
     *   localhost/?page=1             page=1                    ''
     *   localhost/posts?page=1        posts&page=1              posts
     *   localhost/posts/index         posts/index               posts/index
     *   localhost/posts/index?page=1  posts/index&page=1        posts/index
     *
     * A URL of the format localhost/?page (one variable name, no value) won't
     * work however. (NB. The .htaccess file converts the first ? to a & when
     * it's passed through to the $_SERVER variable).
     *
     * @param string $url The full URL
     *
     * @return string The URL with the query string variables removed
     */
    protected function removeQueryStringVariables($url)
    {
        if ($url != '') {
            $parts = explode('&', $url, 2);

            if (strpos($parts[0], '=') === false) {
                $url = $parts[0];
            } else {
                $url = '';
            }
        }

        return $url;
    }

    /**
     * Get the namespace for the controller class. The namespace defined in the
     * route parameters is added if present.
     *
     * @return string The request URL
     */
    protected function getNamespace()
    {
        $namespace = 'AppControllers\';

        if (array_key_exists('namespace', $this->params)) {
            $namespace .= $this->params['namespace'] . '\';
        }

        return $namespace;
    }
}

why cannot connect to SQLITE [duplicate]

   IlluminateDatabaseQueryException 

  could not find driver (Connection: sqlite, SQL: select exists (select 1 from "main".sqlite_master where name = 'migrations' and type = 'table') as "exists")

  at vendorlaravelframeworksrcIlluminateDatabaseConnection.php:822
    818▕                     $this->getName(), $query, $this->prepareBindings($bindings), $e
    819▕                 );
    820▕             }
    821▕ 
  ➜ 822▕             throw new QueryException(
    823▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
    824▕             );
    825▕         }
    826▕     }

  1   vendorlaravelframeworksrcIlluminateDatabaseConnectorsConnector.php:67
      PDOException::("could not find driver")

  2   vendorlaravelframeworksrcIlluminateDatabaseConnectorsConnector.php:67
      PDO::connect()

How to handle sketches in Composer-based projects [duplicate]

I need to sketch out some behavior from a dependency (GuzzleHttpClient) installed via composer. How do I finagle my include_path in a sketch (located under <project root>/sketches) such that I can use GuzzleHttpClient in my sketch?

Just so you can see what I’m doing, here’s my sketch in its entirety.

<?php

use GuzzleHttpClient;

$client_id = 'my_client_id';
$username = 'my_username';
$password = 'bad_password';
$client = new Client;

$client->post(
    "https://my.sso.site/protocol/openid-connect/token",
    [
        'client_id' => $client_id,
        'username' => $username,
        'password' => $password,
        'grant_type' => 'password'
    ]
);

I want to see what exception gets thrown

How to Register and Display a Custom Post Type for Books in WordPress?

I’m building a WordPress site for a bookshop and want to create a custom post type called “Books” to manage book entries. I’ve added the following code to my theme’s functions.php file to register the post type:

function register_book_post_type() {
    $args = array(
        'public' => true,
        'label'  => 'Books',
        'supports' => array('title', 'editor', 'thumbnail'),
        'has_archive' => true,
        'show_in_menu' => true
    );
    register_post_type('book', $args);
}
add_action('init', 'register_book_post_type');

I expected to see “Books” in the WordPress admin menu and create new book entries, but the post type doesn’t appear. I’m using WordPress 6.4 and PHP 8.0. I tried flushing permalinks via Settings > Permalinks, but it didn’t help. What am I missed in making the custom post type visible and usable?

How to pass custom variables in Laravel notifications?

I’m using Laravel notifications to send email notification now I’ve to change the logo of the header dynamically so I need to pass variable from the notification class.

return (new MailMessage)
            ->subject($this->emailContent['subject'])
            ->line(new HtmlString($this->emailContent['email_body']))
            ->action('Pay Invoice', url('/'))
            ->line('Thank you for using our application!')
            ->attachData(base64_decode($this->pdf), 'invoice.pdf', [
                'mime' => 'application/pdf',
            ])
            ->with(['logo' => getEmailTemplateLogo($this->invoice->business)]);

I see many blogs suggesting with() but it also not working. Am I missing here? Could someone please guide?

I’m using the default header file which I get by publishing the vendor:publish resourcesviewsvendormailhtmlheader.blade.php

@props(['url','logo'])
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
<img src="{{ $logo }}" style="width:100px;"> // not getting logo here
</a>
</td>
</tr>

Why does password_hash() gives me a fatal error? [duplicate]

I’m coding a website with a DB for register/login purposes.
I’ve got the setup of the DB and the connection figured out, but hashing the password is my main problem, at the moment.

For some reason, it doesn’t work and I’m out of ideas to the reason.

The code for the connection to the DB and for hashing (ignore the comments):
(I’m using PHP 8.4.7)

<?php

session_start();
require_once 'server.php';  #conectar á BD

if(isset($_POST ['criar_conta'])){  #se o botão de registar foi clicado: 
    $nome = $_POST['username'];
    $email = $_POST['email'];
    $password = password_hash($_POST['password'],  PASSWORD_DEFAULT);

    $verEmail = $conn->query("SELECT email FROM login  WHERE email = '$email'");  #verificar se o email inserido não está na BD
    if ($verEmail-> num_rows > 0) { 
        $_SESSION['erro_registo'] = 'Email já foi registado'; # se estiver, informa o user mas não continua o processo e nao poe na BD
        $_SESSION['active_form'] = 'registo';
    } else {
        $conn->query("INSERT INTO login (nome, email,  password) VALUES('$nome', '$email',  '$password')"); #se o email nao estiver na BD, insere todos os dados fornecidos na BD
    }
    
    header("Location: pagina-login.php"); #apos isto tudo, redireciona para a pagina de login
    exit();
}
?>

I think it’s also worth noting, that in VSCODE, the “PASSWORD_DEFAULT” doesn’t turn any color, its white as if its not recognized.

When I try to insert data into the DB, the webpage redirects me to this error:

“Fatal error: Call to undefined function password_hash() in
C:xampphtdocsWebsite-PAPregisto-data.php on line 9”

(Website-PAP is the name of the folder I’m using to save the project, not a library)

Any help would be appreciated.

Reinstalling PHP;
Read the password_hash manual from PHP and found nothing wrong;
No typos.

php cURL having issues with special characters in passwords

I’m encountering an issue with 2 separate API providers who are insisting this is a problem in our code. Maybe so, but I am not sure what else I need to do here.

Say a user has a password of abc%123. Normally when posting with cURL it handles the encoding.

The cURL code is pretty straight forward:

    $params = [
      'userid' => $_POST['username'],
      'userpw' => $_POST['password']
    ];
    $url = 'https://api-service.com/whatever';
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

    $headers = array("Content-Type: application/json");
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    
    $data = json_encode(array('data' => $params));
    
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
    
    $resp = curl_exec($curl);
    curl_close($curl);
    
    $response = json_decode($resp);

The error I get back, which is specific to the services, more or less say “The submission failed because it contained non-standard characters. Please contact the website administrator to request that they update the settings to allow submission of non-standard characters.”

It sounds like they are asking for the password to be urlencoded. When I do that, it accepts the transport, but then errors saying the password is incorrect as though it is literally reading my posted password as “abc%25123”

Am I missing something here? Some cURL setting or header setting that I just somehow didn’t know about after all these years? To me this seems like the service is disallowing special characters and they need to handle that case, but they both separately insist this is not the case. Any ideas?

Laravel SQL database user posts tags interection (post suggestions)

This is a Laravel 10 project and, I have a table posts – (title(varchar), category (varchar), tags (json), postImg, created_at , delete_at).
And tablet user_post_interactions where I am storing (user_id, post_id, introduction_type).
And I want to show new posts to user based on there post information like tags, category.
How I can do it without any memory overflow (only by using SQL)?

$interactedPostIds = UserPostInteractions::where('user_id', Auth::user()->id)
            ->pluck('post_id');
$page = FacadesRequest::get('page', 1);
$perPage = 10;
//Get hashtags from those posts
$interactedHashtags = Posts::whereIn('id', $interactedPostIds)
            ->pluck('tags')
            ->map(function ($tags) {
                return json_decode($tags); // Convert JSON string to array
            })
            ->flatten()->countBy()->sortDesc()->keys()->toArray();
 $userId = Auth::user()->id;
        $posts =  Posts::where('is_approved', 1)
            ->leftJoin('user_post_interactions', function ($join) use ($userId) {
                $join->on('posts.id', '=', 'user_post_interactions.post_id')
                    ->where('user_post_interactions.user_id', '=', $userId);
            })
            ->selectRaw('posts.*, GROUP_CONCAT(DISTINCT user_post_interactions.interaction_type) as interaction_types')
            ->groupBy('posts.id') // Ensure unique posts
            ->get();
 // Sort using tags match
        $sorted = $posts->sortByDesc(function ($post) use ($interactedHashtags) {
            $postTags = json_decode($post->tags, true) ?? [];
            return count(array_intersect($postTags, $interactedHashtags));
        });
        $posts = new LengthAwarePaginator(
            $sorted->forPage($page, $perPage),
            $sorted->count(),
            $perPage,
            $page,
            ['path' => FacadesRequest::url(), 'query' => FacadesRequest::query()]
        );

Currently, I just pluck post_id from user_post_interactions and by using it I pluck ‘tags’ from post table and after that match these tags for rank posts by tags and take 10 posts to show to user, but when there is more than 1k+ posts it’s going to crash (because of PHP memory limit), is there any other way (using SQL or something else) to do without going to crash?

Localized routes issue with Laravel Inertia SSR apps

I want to have a seemingly simple outcome:

  • website in two languages with URL structure like /en/blog/article1 and /de/blog/seite1
  • content of the page is visible to search engines for SEO purposes

It’s not much, but it turns out to be incredibly difficult to achieve.

Problem

  1. I’m getting urls like this in SSR (Server-side rendering) output: /blog and /blog/article1
  2. When react starts working in the browser, those URLs are corrected into /de/blog and /de/blog/setie1. So to users and to me during development it looks like everything is fine
  3. However, since google sees /blog and /blog/article1, it tries to visit those
  4. /blog is redirected to /de/blog which is problematic but works. The real issue is with /blog/article1 which is redirected to default locale /de/blog/article1 and throws 404, since in German the correct URL is /de/blog/seite1 (article1 post doesn’t exist there).

Stack

Stack is Laravel 11 + Inertia React with SSR enabled. This setup works without issues until you start with localization.

For localization I’ve installed https://github.com/mcamara/laravel-localization.

Hacky fix

I found that incorrect URLs come from this route function definition in ssr.jsx:

import { Ziggy } from '@/ziggy';
global.route = (name, params, absolute, config = Ziggy) 
    => route(name, params, absolute, config);

After days of trying to sort it out, I managed to pull localized URLs into SSR by pulling them from the props where they are put from HandleInertiaRequest middleware:

$ziggy = new Ziggy($group = null, $request->url());
return [
    // Add in Ziggy routes for SSR
    'ziggy' => $ziggy->toArray(),
    ...
]

And in ssr.jsx:

const ziggyConfig = { ...props.initialPage.props.ziggy };
global.route = (name, params, absolute) 
    => route(name, params, absolute, ziggyConfig);

This made URLs localized, but only worked properly on the home page, because all URLs suddenly became relative. Even setting absolute to true did not help. URLs became e.g. /en/contact/en/blog if I was looking at the blog link while being on the /en/contact page.

So to make URLs absolute I introduced this hack – I’m overwriting the “current URL” in ziggy to / to make URLs absolute:

const ziggyConfig = { ...props.initialPage.props.ziggy };
ziggyConfig.url = ziggyConfig.url.split('/').slice(0, 3).join('/');
global.route = (name, params) => route(name, params, true, ziggyConfig);

Which is obviously a dangerous hack and will probably have side effects. However, this currently works and all URLs seem to be correct.

Questions

The main question is how do I make this correctly without this hack?

My other problem is that I can’t really trust the SSR output anymore. What I see on the page locally during development and what I see on production is NOT the same that search engines see. This is really dangerous. How can one test this SSR setup? How can I trust that the website is working if I can’t even see how search engines see it? Any advice here would be super helpful.

I have considered not using react for “public” pages, but would like to avoid this if possible, since that would lead to having to duplicate layout/components.

Problem with wordpress custom post type url structure

I generate a tour post type with tour-type taxonomy:

<?php

add_action('init', 'site_register_taxonomy');
function site_register_taxonomy()
{
        register_taxonomy('tour-type', ['tour'], array(
        'labels' => array(
            'name'              => 'region',
            'all_items'         => 'all regions',
            'edit_item'         => 'edit region',
            'update_item'       => 'update region',
            'add_new_item'      => 'add new region',
        ),
        'public' => true,
        'hierarchical' => true,
        'rewrite' => array(
            'slug' => 'tour',
            'with_front' => false,
            'hierarchical' => true,
        ),
        'show_admin_column' => true,
        'show_in_rest' => false,
    ));
    }

add_filter('post_type_link', 'tour_custom_permalink', 10, 2);
function tour_custom_permalink($post_link, $post) {
    if ($post->post_type !== 'tour') return $post_link;

    $terms = get_the_terms($post->ID, 'tour-type');
    if (!empty($terms) && !is_wp_error($terms)) {
        return str_replace('%tour-type%', $terms[0]->slug, $post_link);
    } else {
        return str_replace('%tour-type%', 'uncategorized', $post_link);
    }
}

add_action('init', 'create_post_type');
function create_post_type()
{
        register_post_type('tour', array(
        'label' => 'تور',
        'public' => true,
        'rewrite' => array(
            'slug' => 'tour/%tour-type%',
            'with_front' => false,
            'hierarchical' => true
        ),
        'has_archive' => 'tour',
        'supports' => array('title', 'editor', 'thumbnail'),
        'taxonomies' => array('tour-type'),
        'show_ui' => true,
        'show_in_menu' => true,
        'show_in_admin_bar' => true,
        'hierarchical' => true,
        'menu_icon' => 'dashicons-admin-site-alt',
        'labels' => array(
            'name' => 'tour',
            'singular_name' => 'tour',
            'menu_name' => 'tour',
            'add_new_item' => 'add tour',
            'edit_item' => 'edit tour',
            'new_item' => 'new tour',
            'view_item' => 'show',
            'search_items' => 'search tour',
            'not_found' => 'not found',
            'not_found_in_trash' => 'not found in trash',
        )
    ));
}

now the result of hierarchical taxonomy is like this:

site.com/tour/turkey/istanbul/istanbul-from-dubai/

for single post is like this:

site.com/tour/turkey/istanbul/istanbul-from-dubai/istanbul-from-dubai-flight-5days

the problem is that I get 404 error for single posts.

another question is how can I shorten my url when I have two similar slug.

for example: I create from-dubai for istanbul parent and another for izmir. when I create number two slug it’s changed to this: from-dubai-izmir. I think that is the behaviour of wordpress for slugs.This behavior is the same when creating single posts. how to change it?

If you have a solution for these two problems, it would be appreciated.

How can I add additional fees/items from the checkout page in a WooCommerce store?

I have a WooCommerce store specialized in renting out beach equipment. I added a dropdown menu to the checkout page where a customer can select for the order to be delivered or that they can pick it up themselves. If they select the delivery option, a $10 fee is added to the cart total, listed as Delivery Fee. If they select pickup, a second dropdown appears where they can select a pickup location. I want to add the option for clients to purchase cold drinks from the checkout page if they select a specific pickup location (Kamini’s Kitchen), the list of which should pop up when they select this one pickup location. I wrote it so that checkboxes appear for each drink and a quantity that can be selected. When a drink is added, it should be added to the cart under its proper name and the listed price should be added to the total (times the multiplier). Lastly, the selected drink options should appear in the order e-mails and on the admin backend.

The problem is that when a drink is selected from the list, it doesn’t get added to the cart total. The cart does refresh, but nothing gets added. I have existing code that some of the folks over here helped me write, but the drink extensions part that I’m trying to do is not working for me. I made an array for all the drinks and the dollar amounts, and JS code to make the list appear conditionally. The part where they’re supposed to be added to the cart dynamically through AJAX is where I’m stuck.

Here is my code so far:

// Utility function: Get Delivery or Pickup options
function get_delivery_or_pickup_options() {
    return array(
        ''          => esc_html__( 'Select an option', 'woocommerce' ),
        'delivery'  => esc_html__( 'Delivery Service', 'woocommerce' ),
        'pickup'    => esc_html__( 'Pickup', 'woocommerce' )
    );
}

// Utility function: Get pickup location options
function get_pickup_location_options() {
    return array(
        ''                  => esc_html__( 'Select an option', 'woocommerce' ),
        'main_office'       => esc_html__( 'Pavia 20F - Main Office', 'woocommerce' ),
        'kaminis_kitchen'   => esc_html__( "Kamini's Kitchen (Baby Beach)", 'woocommerce' )
    );
}

add_action( 'woocommerce_before_order_notes', 'add_delivery_or_pickup_fields', 10 );
function add_delivery_or_pickup_fields( $checkout ) {
    // Existing dropdowns...
    woocommerce_form_field( 'delivery_or_pickup', array(
        'type'      => 'select',
        'class'     => array('form-row-wide'),
        'label'     => esc_html__( 'Delivery or Pickup?', 'woocommerce' ),
        'required'  => true,
        'options'   => get_delivery_or_pickup_options(),
    ), $checkout->get_value('delivery_or_pickup') );

    woocommerce_form_field('pickup_location', array(
        'type' => 'select',
        'class' => array('form-row-wide'),
        'label' => esc_html__( 'Select pickup location:', 'woocommerce' ),
        'required' => true,
        'options' => get_pickup_location_options(),
    ), $checkout->get_value('pickup_location'));

    // Drinks Section - Hidden by default, displayed by JS if pickup_location == kaminis_kitchen
    echo '<div id="drink_options_section" style="display:none;">';
    echo '<h3>' . esc_html__('If you’re renting a beach cooler, you have the option to pre-purchase drinks here and have the cooler loaded up with ice and drinks when picking it up', 'woocommerce') . '</h3>';

    $drink_items = array(
        'bag_of_ice'       => array('Bag of Ice', 4.00),
        'chill'            => array('Chill', 5.00),
        'balashi'          => array('Balashi', 5.00),
        'magic_mango'      => array('Magic Mango', 5.00),
        'amstel_bright'    => array('Amstel Bright', 6.00),
        'budweiser'        => array('Budweiser', 5.00),
        'bud_light'        => array('Bud Light', 5.00),
        'corona'           => array('Corona', 6.00),
        'heineken'         => array('Heineken', 5.00),
        'presidente'       => array('Presidente', 5.00),
        'modelo'           => array('Modelo', 5.00),
        'polar'            => array('Polar', 4.00),
        'guinness'         => array('Guinness', 7.00),
        'coke'             => array('Coke', 3.00),
        'coke_zero'        => array('Coke Zero', 3.00),
        'sprite'           => array('Sprite', 3.00),
        'sprite_zero'      => array('Sprite Zero', 3.00),
        'gingerale'        => array('Gingerale', 3.00),
        'fuze_tea'         => array('Fuze Tea', 3.00),
        'club_soda'        => array('Club Soda', 3.00),
        'tonic_water'      => array('Tonic Water', 3.00),
        'fanta_cherry'     => array('Fanta Cherry', 3.00),
        'fanta_grape'      => array('Fanta Grape', 3.00),
        'fanta_orange'     => array('Fanta Orange', 3.00),
        'bottled_water'    => array('Bottled Water', 2.57),
    );

    foreach ( $drink_items as $key => $item ) {
        echo '<p><label><input type="checkbox" name="drinks[' . esc_attr($key) . ']" value="1"> ' . esc_html($item[0]) . ' ($' . number_format($item[1], 2) . ')</label> ';
        echo '<input type="number" name="drink_qty[' . esc_attr($key) . ']" min="1" max="99" value="1" style="width:60px;margin-left:10px;" /></p>';
    }

    echo '</div>';
}


// JavaScript to toggle visibility of the pickup location dropdown and update delivery fee
add_action( 'woocommerce_checkout_init', 'add_delivery_or_pickup_js_script' );
function add_delivery_or_pickup_js_script() {
    wc_enqueue_js("

    const pickupField = $('#pickup_location_field');
    const drinkOptions = $('#drink_options_section');

    function toggleDrinkOptions() {
        if ($('#pickup_location').val() === 'kaminis_kitchen') {
            drinkOptions.show();
        } else {
            drinkOptions.hide();
        }
    }

    if ($('#delivery_or_pickup').val() !== 'pickup') {
        pickupField.hide();
        drinkOptions.hide();
    }

    $(document.body).on('change', '#delivery_or_pickup', function() { 
        const val = $(this).val();
        val === 'pickup' ? pickupField.show() : pickupField.hide();
        $.post(wc_checkout_params.ajax_url, {
            action: 'save_delivery_or_pickup_to_session',
            delivery_or_pickup: val
        }, function() {
            $(document.body).trigger('update_checkout');
        });
    });

    $(document.body).on('change', '#pickup_location', function() {
        toggleDrinkOptions();
        $(document.body).trigger('update_checkout');
    });

    // Send drink selections via AJAX
    function updateDrinkSessionData() {
        const selectedDrinks = {};
        $('#drink_options_section input[type=checkbox]:checked').each(function() {
            const key = $(this).attr('name').replace('drinks[', '').replace(']', '');
            const qty = $('#drink_qty_' + key).val() || 1;
            selectedDrinks[key] = qty;
        });

        $.post(wc_checkout_params.ajax_url, {
            action: 'save_drink_options_to_session',
            drinks: selectedDrinks
        }, function() {
            $(document.body).trigger('update_checkout');
        });
    }

    $(document.body).on('change', '#drink_options_section input', updateDrinkSessionData);

    toggleDrinkOptions();
");
}


// Ajax request: Save selection to WooCommerce session
add_action('wp_ajax_save_delivery_or_pickup_to_session', 'save_delivery_or_pickup_to_session');
add_action('wp_ajax_nopriv_save_delivery_or_pickup_to_session', 'save_delivery_or_pickup_to_session');
function save_delivery_or_pickup_to_session() {
    if ( isset($_POST['delivery_or_pickup']) ) {
        WC()->session->set('delivery_or_pickup', sanitize_text_field($_POST['delivery_or_pickup']));
    }
    wp_die();
}

// Add delivery fee if "Delivery Service" is selected
add_action( 'woocommerce_cart_calculate_fees', 'add_delivery_option_fee_based_on_session' );
function add_delivery_option_fee_based_on_session( $cart ) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    if ( is_checkout() && WC()->session->get('delivery_or_pickup') === 'delivery' ) {
        $delivery_fee = 10;
        $cart->add_fee( esc_html__( 'Delivery Fee', 'woocommerce' ), $delivery_fee );
    }
}

// Save drink selection to WooCommerce session
add_filter('woocommerce_checkout_posted_data', 'capture_drink_data_during_ajax');
function capture_drink_data_during_ajax( $data ) {
    if ( isset($_POST['drinks']) && isset($_POST['drink_qty']) ) {
        $data['drinks'] = $_POST['drinks'];
        $data['drink_qty'] = $_POST['drink_qty'];
    }
    return $data;
}

add_action('wp_ajax_save_drink_options_to_session', 'save_drink_options_to_session');
add_action('wp_ajax_nopriv_save_drink_options_to_session', 'save_drink_options_to_session');
function save_drink_options_to_session() {
    $drinks = isset($_POST['drinks']) ? $_POST['drinks'] : array();
    $sanitized = array();

    foreach ($drinks as $drink => $qty) {
        $drink = sanitize_key($drink);
        $qty = max(1, intval($qty));
        $sanitized[$drink] = $qty;
    }
    
    WC()->session->set('drink_data', $sanitized);
    wp_die();
}


add_action('woocommerce_cart_calculate_fees', 'add_drink_fee_via_ajax', 20, 1);
function add_drink_fee_via_ajax($cart) {
    if (is_admin() && !defined('DOING_AJAX')) return;

    $pickup_location = isset($_POST['pickup_location']) ? sanitize_text_field($_POST['pickup_location']) : WC()->session->get('pickup_location');
    if ($pickup_location !== 'kaminis_kitchen') return;

    $drink_data = WC()->session->get('drink_data', []);
    if (empty($drink_data)) return;

    $prices = array(
        'bag_of_ice' => 4.00, 'chill' => 5.00, 'balashi' => 5.00, 'magic_mango' => 5.00,
        'amstel_bright' => 6.00, 'budweiser' => 5.00, 'bud_light' => 5.00, 'corona' => 6.00,
        'heineken' => 5.00, 'presidente' => 5.00, 'modelo' => 5.00, 'polar' => 4.00, 'guinness' => 7.00,
        'coke' => 3.00, 'coke_zero' => 3.00, 'sprite' => 3.00, 'sprite_zero' => 3.00,
        'gingerale' => 3.00, 'fuze_tea' => 3.00, 'club_soda' => 3.00, 'tonic_water' => 3.00,
        'fanta_cherry' => 3.00, 'fanta_grape' => 3.00, 'fanta_orange' => 3.00, 'bottled_water' => 2.57,
    );

    $total = 0;
    foreach ($drink_data as $drink => $qty) {
        if (isset($prices[$drink])) {
            $total += $prices[$drink] * $qty;
        }
    }

    if ($total > 0) {
        $cart->add_fee(__('Drink Options', 'woocommerce'), $total);
    }
}


add_action('woocommerce_checkout_order_processed', function () {
    WC()->session->__unset('drink_data');
}, 10, 1);

// Validate custom shipping options
add_action( 'woocommerce_after_checkout_validation', 'delivery_or_pickup_validation', 20, 2 );
function delivery_or_pickup_validation( $data, $errors ) {
    // Delivery or Pickup validation
    if ( isset($_POST['delivery_or_pickup']) && empty($_POST['delivery_or_pickup']) ) {
        $errors->add( 'delivery_or_pickup', esc_html__( 'You must choose between "Delivery" or "Pickup" option.', 'woocommerce' ), 'error' );
    }
    // Pickup location validation
    elseif ( isset($_POST['delivery_or_pickup']) && $_POST['delivery_or_pickup'] === 'pickup' 
    && isset($_POST['pickup_location']) && empty($_POST['pickup_location']) ) {
        $errors->add( 'pickup_location', esc_html__( 'You must choose a pickup location.', 'woocommerce' ), 'error' );
    }
}

// Save custom chosen shipping option details as order metadata
add_action( 'woocommerce_checkout_create_order', 'save_delivery_or_pickup_as_order_metadata', 10 );
function save_delivery_or_pickup_as_order_metadata( $order ) {
    if ( isset($_POST['delivery_or_pickup']) && !empty($_POST['delivery_or_pickup']) ) {
        $order->add_meta_data('_delivery_or_pickup', esc_attr($_POST['delivery_or_pickup']), true );
    }
    if ( isset($_POST['pickup_location']) && !empty($_POST['pickup_location']) ) {
        $order->add_meta_data('_pickup_location', esc_attr($_POST['pickup_location']), true);
    }
    // Remove the WC Session Variable
    if (  WC()->session->__isset('delivery_or_pickup') ) {
        WC()->session->__unset('delivery_or_pickup');
    }
}

add_action( 'woocommerce_checkout_create_order', 'save_drinks_to_order_meta', 20, 1 );
function save_drinks_to_order_meta( $order ) {
    if ( isset($_POST['drinks']) ) {
        $items = array(
            'bag_of_ice' => 'Bag of Ice', 'chill' => 'Chill', 'balashi' => 'Balashi', 'magic_mango' => 'Magic Mango',
            'amstel_bright' => 'Amstel Bright', 'budweiser' => 'Budweiser', 'bud_light' => 'Bud Light',
            'corona' => 'Corona', 'heineken' => 'Heineken', 'presidente' => 'Presidente',
            'modelo' => 'Modelo', 'polar' => 'Polar', 'guinness' => 'Guinness', 'coke' => 'Coke',
            'coke_zero' => 'Coke Zero', 'sprite' => 'Sprite', 'sprite_zero' => 'Sprite Zero',
            'gingerale' => 'Gingerale', 'fuze_tea' => 'Fuze Tea', 'club_soda' => 'Club Soda',
            'tonic_water' => 'Tonic Water', 'fanta_cherry' => 'Fanta Cherry', 'fanta_grape' => 'Fanta Grape',
            'fanta_orange' => 'Fanta Orange', 'bottled_water' => 'Bottled Water'
        );

        $drinks_ordered = array();
        foreach ($_POST['drinks'] as $key => $val) {
            $qty = intval($_POST['drink_qty'][$key] ?? 1);
            if ($qty > 0 && isset($items[$key])) {
                $drinks_ordered[] = $items[$key] . ' x' . $qty;
            }
        }

        if ( !empty($drinks_ordered) ) {
            $order->add_meta_data('_drink_options', implode(', ', $drinks_ordered));
        }
    }
}


// Update custom chosen shipping option details as USER metadata (useful for next checkout)
add_action( 'woocommerce_checkout_update_customer', 'save_delivery_or_pickup_as_user_metadata', 10 );
function save_delivery_or_pickup_as_user_metadata( $customer ) {
    if ( isset($_POST['delivery_or_pickup']) && !empty($_POST['delivery_or_pickup']) ) {
        $customer->update_meta_data('delivery_or_pickup', esc_attr($_POST['delivery_or_pickup']));
    }
    if ( isset($_POST['pickup_location']) && !empty($_POST['pickup_location']) ) {
        $customer->update_meta_data('pickup_location', esc_attr($_POST['pickup_location']));
    }
}

// Display chosen custom shipping option details in the admin panel under shipping address
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'display_delivery_or_pickup_meta_in_admin_order', 10 );
function display_delivery_or_pickup_meta_in_admin_order( $order ) {
    // Delivery or Pickup
    if ( $value = $order->get_meta('_delivery_or_pickup') ) {
        $options = get_delivery_or_pickup_options();
        printf( '<p><strong>%s:</strong> %s', esc_html__('Shipping', 'woocommerce' ), $options[$value] );

        // Pickup location
        if ( $value = $order->get_meta('_pickup_location') ) {
            $options = get_pickup_location_options();
            printf( '<br><strong>%s:</strong> %s', esc_html__('Location', 'woocommerce' ), $options[$value] );
        }
        echo '</p>';
    }
}

// Display chosen custom shipping option details in order total lines (customer orders and email notifications)
add_filter( 'woocommerce_get_order_item_totals', 'insert_custom_line_order_item_totals', 10, 3 );
function insert_custom_line_order_item_totals( $total_rows, $order, $tax_display ){
    $shipping = $order->get_meta('_delivery_or_pickup');
    $options1  = get_delivery_or_pickup_options();
    $location = $order->get_meta('_pickup_location');
    $options2  = get_pickup_location_options();
    $key_target = array_key_exists('discount', $total_rows) ? 'discount' : 'cart_subtotal';
    $new_total_rows = array();

    // Loop through total rows
    foreach( $total_rows as $key => $value ){
        $new_total_rows[$key] = $total_rows[$key];

        if( 'cart_subtotal' === $key ) {
            $new_total_rows['shipping2'] = array(
                'label' => esc_html__('Shipping option:'),
                'value' => $options1[$shipping],
            );

            if ( $location ) {
                $new_total_rows['location'] = array(
                    'label' => esc_html__('Pickup location:'),
                    'value' => $options2[$location],
                );
                $new_total_rows['drink_options'] = array(
    'label' => esc_html__('Drink Options:'),
    'value' => esc_html($order->get_meta('_drink_options')),
);
            }

        }
    }
    return $new_total_rows;
}

How to correctly configure CORS in Laravel 12.13 with the HandleCors middleware in bootstrap/app.php?

I am currently working with Laravel 12.13 and facing an issue with CORS. I have configured the HandleCors middleware and the CORS settings in my application, but I am still getting CORS errors when trying to make requests from the frontend to the backend.

Here’s what I have done so far:

In bootstrap/app.php, I have added the following middleware configuration:

<?php use IlluminateFoundationApplication; 
use IlluminateFoundationConfigurationExceptions;
use IlluminateFoundationConfigurationMiddleware; 
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
    web: __DIR__.'/../routes/web.php',
    api: __DIR__.'/../routes/api.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
    // Register native CORS middleware in Laravel 12
    $middleware->append(IlluminateHttpMiddlewareHandleCors::class);

    // You can add other global middleware here
})
->withExceptions(function (Exceptions $exceptions) {
    //
})
->create();

I have created the cors.php configuration file inside the config directory with the following content:

<?php
return [
'paths' => ['api/v1/*'],
// Methods I want to allow for CORS:
'allowed_methods' => ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE'],
// Origins from where I allow access without CORS interference:
'allowed_origins' => ['https://annaponsprojects.com'],

'allowed_origins_patterns' => [],

// Headers I want to allow to receive in frontend requests:
'allowed_headers' => ['Content-Type', 'Authorization', 'Accept'],

'exposed_headers' => [],
// Don't perform preflight until 1 hour has passed:
'max_age' => 3600,
// Indicates if the browser can send cookies (works with tokens):
'supports_credentials' => false ];

However, I am still encountering CORS issues when sending a request from the frontend to my backend API. The error message I receive is:

Access to fetch at ” from origin ” has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

What have I tried so far?
I’ve made sure to include the correct middleware (HandleCors) in the bootstrap/app.php.

I’ve configured CORS settings in the config/cors.php file.

I’ve cleared the cache (php artisan config:clear and php artisan cache:clear).

What could I be missing?
I suspect the issue might be related to the CORS configuration, but I am not sure. Is there anything else I should check or configure in Laravel 12.13 to resolve this issue?

Using property in another method in same class in different HTTP requests

I’m new to OOP in php and I’ve the following issue.
Basically I’ve visit form which submitted data to create action in the controller.. after that it’s redirect to success action which outputting visit counter depend on submitted data.

My question now is how I can store posted data inside a property in create method then use it in success method ?

Maybe I didn’t explain my issue very well for that please check my code below:

addvisit.php

public function createAction()
    {
        $visit = new Visit($_POST);
        $plate = $_POST['plate'];

        if ($visit->save()) {
            
            $this->redirect('/AddVisit/success');

        } else {

            View::renderTemplate('Visits/new.html', [
                'visit' => $visit
            ]);

        }
    }

    public function successAction()
    {
        $visit = new Visit();
        $visit->countVisit($this->createAction()->plate);

        View::renderTemplate('Visits/success.html', [
                'visit' => $visit
            ]);
    }

I’m trying also to use return in create method like below but it’s didn’t work:

public function createAction()
    {
        $visit = new Visit($_POST);
        $plate = $_POST['plate'];

        if ($visit->save()) {
            
            $this->redirect('/AddVisit/success');
            return $plate;

FYI this is also countVisit method in my model:

public function countVisit($plate)
    {
        $count = static::findCar($plate);

        // Visit less than required qty for free
        if ($this->subscribe->isMember($plate)) {
            $remain = $this->subscribe->isMember($plate)->subscribe_qty;
            echo "Remainnig visit <h3>$remain</h3>";
        } elseif ($count->qty >= 3) {
            $this->resetCount($plate);
            echo "<h3>You got a free visit</h3>";
        } else {
            $current = $count->qty;
            echo "This is the visit number <h3>$current</h3>";
        }

    }

I didn’t put the full code and trying to focus on my problem part.

Thank you.