将文本中的@mentions 替换为可点击的文本,包括从数据库中获取的相关 ID table

Replace @mentions in text with clickable text including related ids fetched from a database table

我希望能够点击每个@.... 并转到他们的特定页面,这样我就可以从数据库中获取文本中每个匹配项的 ID,以便 link比赛的页面。我确实从 foreach 循环中获取了一个 id 数组,但是当我在 foreach 循环中使用 preg_replace 时,我为多个值插入了相同的 id。我被卡住了,尝试了很多不同的变体,但还没有成功。

$text = "I went to the dog park yesterday and saw @dog4 playing with @dog8 and @dog3 drinking water.";

public function getLinks($text) {

    preg_match_all("/@([\w]+)/", $text, $matches);

if ($matches) {
    $result = array_values($matches[1]);
}

$sql = "SELECT dogId FROM dogs WHERE dogName = :dogName";

foreach ($result as $dogName) {
    $stmt = $this->con->prepare($sql); 
    $stmt->execute(array(":dogName" => $dogName));
    $data = $stmt->fetch(PDO::FETCH_ASSOC);
    
    $i = 0;
    if (!empty($data)) {
        
        foreach ($data as $dogId) {
            $i++;
            // It's working here.  I'm getting an array of dogIds
            echo '<pre>'; print_r($data); echo '</pre>';
            Array
            (
                [dogId] => 4
            )
            Array
            (
                [dogId] => 8
            )
            Array
            (
                [dogId] => 3
            )
            if ($i == count($data)) {
                $pattern = "/@([\w]+)/";
                $dogPage = "<span onclick='openPage(\"dogs.php?id=$dogId\")' role='link' tabindex='0'>[=11=]</span>";
                $dogLink = preg_replace($pattern, $dogPage, $text);

                // It's not working here.  I only get the last array value(3) inserted in $dogId for every match.
                echo '<pre>'; print_r($dogPage); echo '</pre>';
                "<span onclick='openPage(\"dogs.php?id=3\")' role='link' tabindex='0'>@dog4</span>"
                "<span onclick='openPage(\"dogs.php?id=3\")' role='link' tabindex='0'>@dog8</span>"
                "<span onclick='openPage(\"dogs.php?id=3\")' role='link' tabindex='0'>@dog3</span>"
            }           
        }                   
    } 
} return $dogLink
}

我得到的结果文本是

I went to the dog park yesterday and saw @dog4(id=3) playing with @dog8(id=3) and @dog3(id=3) drinking water.

但我想要实现的是

I went to the dog park yesterday and saw @dog4(id=4) playing with @dog8(id=8) and @dog3(id=3) drinking water.

提前致谢!

首先扫描您的输入文本以查找所有@mentions 并构建一个没有前导“@”的平面值数组(\K 意味着忘记任何先前匹配的字符——在本例中为 @ ).从生成列表中的 dogs table 中提取所有行,并将它们转换为查找数组,其中 dogName 作为键,dogId 作为值。

$count = preg_match_all('/@\K\w+/', $text, $mentions);
if ($count) {
    $in  = str_repeat('?,', $count - 1) . '?';
    $stmt = $db->prepare("SELECT dogName, dogId FROM dogs WHERE dogName IN ($in)");
    $stmt->execute($mentions[0]);
    $lookup = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}

生成$mentions的演示:https://3v4l.org/O9v37
PDO 引用: & https://phpdelusions.net/pdo/fetch_modes#FETCH_KEY_PAIR

然后您可以使用 /@(\w+)/ 作为 preg_replace_callback() 的第一个参数再次读取 @mentions 的输入文本。在回调的自定义函数范围内,使用 isset() 快速检查匹配的提及项是否作为查找中的键存在——如果是,则替换,如果不是,则不要更改文本。

$text = "I went to the dog park yesterday and saw @dog4 playing with @dog8 and @dog3 drinking water -- poor @dog33.";

$lookup = [
    'dog4' => 4,
    'dog8' => 8,
    'dog3' => 3,
];

echo preg_replace_callback(
         '/@(\w+)/',
         function ($m) use($lookup) {
             return isset($lookup[$m[1]])
                        ? "<span onclick='openPage(\"dogs.php?id={$lookup[$m[1]]}\")' role='link' tabindex='0'>{$m[0]}</span>"
                        : $m[0];
         },
         $text
     );

演示:https://3v4l.org/7Z2Wp

输出:

I went to the dog park yesterday and saw <span onclick='openPage("dogs.php?id=4")' role='link' tabindex='0'>@dog4</span> playing with <span onclick='openPage("dogs.php?id=8")' role='link' tabindex='0'>@dog8</span> and <span onclick='openPage("dogs.php?id=3")' role='link' tabindex='0'>@dog3</span> drinking water -- poor @dog33.