---
# System prepended metadata

title: PHP

---

# PHP
[TOC]

## PHP 內建函數

### PHP str
1. strip_tags 清除html程式碼
```php=
<?php
$mystring="<a href='http://www.webtech.tw'>WebTech</a>";
echo strip_tags($mystring);
//輸出
WebTech
?>
```
2. rtrim 可以去除最右邊的
```php=
$date_search .= " data_date LIKE '".$item["data_date"]."' OR";
$date = rtrim($date_search, 'OR');
```
3. str_pad 補0輸出
```php=
$month = 3; // 例如，3月
$day = 5;   // 例如，5日
// 补零并输出
$formattedMonth = str_pad($month, 2, "0", STR_PAD_LEFT);
$formattedDay = str_pad($day, 2, "0", STR_PAD_LEFT);
```

4. floor 小數捨去
```php=
<?php
floor(intval($buy_sell)/1000*100)/100,
```
5. round 四捨五入
```php=
<?php
round(intval($amount),2);
```

### PHP Array
1. array_push 陣列增加
```php=
<?php
$array = array();
array_push($array,"apple");
```

2. array_values 將索引重置
```php=
<?php
$data = array_values($data);
?>
```

3. array_search 尋找檔名在Array value的key
```php=
<?php
$tmp_img_sorts = explode(";",$request->tmp_img_sort);
foreach ($request->file('multiple_img') as $key=>$image) {
    $originalName =  $image->getClientOriginalName();
    $filekey = array_search($originalName, $tmp_img_sorts);
    $multiple_imgs[$filekey];
    //這邊就是陣列去尋找原本檔名的在地幾個key後然後儲存
}
```

4. in_array 查看資料有沒有在Array的Value裡
```php=
<?php
$fruits = array("apple", "banana", "orange", "grape");
// 检查数组中是否存在某个值
$searchValue = "orange";
if (in_array($searchValue, $fruits)) {
    echo "$searchValue 存在于数组中。";
} else {
    echo "$searchValue 不存在于数组中。";
}
```

5. unset 刪除array指定key
```php=
<?php
unset($data[0]);
?>
```

6. array_slice array取出一段值
```php=
<?php
$a=array("red","green","blue","yellow","brown");
print_r(array_slice($a,1,2));
// 輸出
Array ( [0] => green [1] => blue )
?>
```

7. array_filter 陣列篩選，或是將空值過濾
```php=
<?php
$search_conditions = array_filter($search_conditions);
//也可以直接篩選
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 過濾出偶數
$evenNumbers = array_filter($numbers, function($number) {
    return $number % 2 === 0;
});
```

8. array_diff 找出兩組陣列不同差異
```php=
<?php
$array1 = [1, 2, 3, 4, 5];
$array2 = [3, 4, 5, 6, 7];

$onlyInArray1 = array_diff($array1, $array2);
$onlyInArray2 = array_diff($array2, $array1);

print_r("Values only in Array1: ");
print_r($onlyInArray1);

print_r("Values only in Array2: ");
print_r($onlyInArray2);

Values only in Array1: Array
(
    [0] => 1
    [1] => 2
)

Values only in Array2: Array
(
    [0] => 6
    [1] => 7
)
```

## PHP 作業系統判別

```php=
<?php
if (strtoupper(substr(PHP_OS, 0, 5)) === 'LINUX') {
    //linux
}elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
    //windows
}
```

## PHP Json轉資料
```php=
<?php
// 轉成json資料
try {
} catch (\Exception $e) { // dd($e);
return json_encode([
                    'status' => 'fail',
                    'msg' => '查詢失敗',
                    'e' => $e
                ]);
}
// json轉回php array or object
json_decode();
```

## PHP查看版本
1. cmd指令
php -v
2. PHP裡輸出
```php=
<?php
phpinfo();

echo PHP_VERSION
```

## PHP 日期計算

```php=
<?php
給目前時間加一天、一小时

echo "今天:",date('Y-m-d H:i:s'),"<br>";
echo "明天:",date('Y-m-d H:i:s',strtotime('+1 day'));
同理，加一年、一個月、一小時、一分鐘…

echo "明天:",date('Y-m-d H:i:s',strtotime('+1 day'));

echo "明天:",date('Y-m-d H:i:s',strtotime('+1 hour'));

echo "明天:",date('Y-m-d H:i:s',strtotime('+1 minute'));

echo "明天:",date('Y-m-d H:i:s',strtotime('+1 mouth'));

//這裡+1 day可以修改參數1為任何想需要的數 day也可以改成year（年），month（月），hour（小時）minute（分），second（秒）如：
date('Y-m-d H:i:s',strtotime("+1 day +1 hour +1 minute");

//给指定時間加一天、一小時

echo date("Y-m-d",strtotime("+1 month",strtotime("2020-10-13")));
?>
```

### PHP 簡易日期區間判斷星期幾
```php=
<?php
$weekarray=array("日","一","二","三","四","五","六");
$start_date = "2023-07-01";
$start_strtotime = strtotime($start_date);
$end_date = "2023-08-04";
$end_strtotime = strtotime($end_date);
$days = round(($end_strtotime-$start_strtotime)/3600/24);
for ($i=0;$i<=$days;$i++){
    $now_date = date('Y-m-d',strtotime("-$i day",$end_strtotime));
    $week = $weekarray[date("w",strtotime($now_date))];
    if($week == "六" || $week=="日"){
        echo "$now_date 星期 $week 不開盤<br>";
    }else{
        echo "$now_date 星期 $week 開盤<br>";
    }
}
?>

```

## PHP 計算執行程式多久
```php=
<?php
$start_time = microtime(true);
// 你的 PHP 程式碼

// 記錄結束時間
$end_time = microtime(true);

// 計算執行時間
$execution_time = ($end_time - $start_time);

// 顯示執行時間
echo "程式執行時間： {$execution_time} 秒";
?>
```

## PHP curl爬網頁

### PHP curl get
```php=
<?php
$PostData = "a=abc&b=def";

$curl = curl_init("https://test.com/test.php?$PostData");

//檢查 curl 有無作用
if (! $curl) {
die( "Cannot allocate a new PHP-CURL handle" );
}

curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); //SSL
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//SSL

curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//這個若是沒設 , curl_exec($curl) 會直接印出來


curl_setopt ($curl, CURLOPT_HEADER, 0); // 得到回傳的HTTP頁面.

$data = curl_exec($curl);

echo "&data=$data"; //輸出傳回值

curl_close($curl); //關閉連線
```

### PHP curl post
```php=
<?php
$PostData = "a=abc&b=def";

$curl = curl_init("https://test.com/test.php");

//檢查 curl 有無作用
if (! $curl) {
die( "Cannot allocate a new PHP-CURL handle" );
}

curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); //SSL
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//SSL
//這邊是給Header 下面只是範例 可設可不設
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$channelAccessToken}", "Content-Type: application/json"));
curl_setopt($ch, CURLOPT_HEADER, 1);
//---------------------------
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//這個若是沒設 , curl_exec($curl) 會直接印出來

curl_setopt($curl, CURLOPT_POST,1);//設定CURLOPT_POST為1或true，表示用post傳送

curl_setopt($curl, CURLOPT_POSTFIELDS,$PostData)// PostData傳送的資料

curl_setopt ($curl, CURLOPT_HEADER, 0); // 得到回傳的HTTP頁面.

$data = curl_exec($curl);

echo "&data=$data"; //輸出傳回值

curl_close($curl); //關閉連線
```
### PHP file_get_contents

file_get_contents($url);

## PHP 瀏覽器清除緩存
```php=
<?php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Cache-Control: post-check=0, pre-check=0", false);
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Pragma: no-cache");
```

## PHP 壓縮pdf ghostscript

1. 下載 ghostscript
```php=
<?php
        $pdfPath = public_path('uploads/').$pdf_link;
        $compressedPdfPath = public_path('uploads/').$compressed_pdf_link;
        $ghostscriptPath = '"C:\\Program Files\\gs\\gs10.02.1\\bin\\gswin64c.exe"';
        // $command = "$ghostscriptPath -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile={$compressedPdfPath} {$pdfPath}";
        // $command = "gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile={$compressedPdfPath} {$pdfPath}";linux指令
        $command = "$ghostscriptPath -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -r72 -sOutputFile={$compressedPdfPath} {$pdfPath}";
        $output = shell_exec($command);
```

## PHP 正規式


### 抓取日期
```php=
<?php
    $pattern = '/\b\d{4}\/\d{2}\/\d{2}\b/';
    preg_match($pattern, $value, $matches);
    if (!empty($matches)) {
        $data_date = str_replace("/","-",$matches[0]);
        break;
    }
```

### 去除空白
```php=
<?php
$value = preg_replace('/\s+/', "", $value);
```

### 頭尾去除空白(特殊無法用其他" "取代掉的&nbsp;)

```php=
<?php
$value = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $value);
?>
```

### 將括號內容移除
```php=
<?php
$string = "這是一個（含有括號的）字串。";
$pattern = '/\([^)]*\)/'; // 正規表達式，匹配括號及其內容

$cleanedString = preg_replace($pattern, '', $string);

echo $cleanedString;
```

### 基本匹配：檢查字串中包含是否某個特定的字詞或字符
```php=
<?php
$pattern = '/apple/';
$text = 'I like apples.';
if (preg_match($pattern, $text)) {
    echo "Match found!";
} else {
    echo "No match found.";
}
?>
```
### 案例不敏感匹配：執行不區分大小寫的模式匹配
```php=
<?php
$pattern = '/apple/i';
$text = 'I like Apples.';
if (preg_match($pattern, $text)) {
    echo "Match found!";
} else {
    echo "No match found.";
}
?>
```
### 提取匹配：從字符串中提取匹配的部分
```php=
<?php
$pattern = '/(\d{2})-(\d{2})-(\d{4})/';
$text = 'Date of birth: 05-20-1990';
if (preg_match($pattern, $text, $matches)) {
    echo "Day: " . $matches[1] . ", Month: " . $matches[2] . ", Year: " . $matches[3];
} else {
    echo "No match found.";
}
?>
```
### 替換：將匹配的部分替換為新的字符串 preg_replace
```php=
<?php
$pattern = '/apple/';
$text = 'I like apples.';
$replacement = 'oranges';
$new_text = preg_replace($pattern, $replacement, $text);
echo $new_text; // Output: I like oranges.
?>
```
### 字符範圍：匹配特定範圍的字符
```php=
<?php
$pattern = '/[0-9]/'; // 匹配任何數字字符
$text = 'The code is 12345.';
if (preg_match($pattern, $text)) {
    echo "Match found!";
} else {
    echo "No match found.";
}
?>
```
### 字串裡提取英文
```php=
<?php
$text = "Hello123World456";
$pattern = '/[A-Za-z]+/';
preg_match_all($pattern, $text, $matches);
$english_words = $matches[0];
print_r($english_words);
Array
(
    [0] => Hello
    [1] => World
)
?>
```

### 字串裡提取數字
```php=
<?php
$text = "Hello123World456";
$pattern = '/\d+/';
preg_match_all($pattern, $text, $matches);
$numbers = $matches[0];
print_r($numbers);
Array
(
    [0] => 123
    [1] => 456
)
?>
```

### 獲得輸出richMenuId
```php=
<?php
$pattern = '/"richMenuId":"(.*?)"/';
preg_match($pattern, $output, $matches);
if (isset($matches[1])) {
    $richMenuId = $matches[1];
    echo "成功獲得 Rich Menu ID：$richMenuId<br>";
} else {
    echo "無法提取 Rich Menu ID。<br>";
}
?>
```


# PHP Laravel 

(https://hackmd.io/NWZ9E6EQQ4yUVBFX-Je8hQ)

# .htaccess

## 檔案數量限制及size
```php=
php_value upload_max_filesize 50M
php_value post_max_size 52M
php_value max_file_uploads 100

<IfModule mod_rewrite.c>
    RewriteEngine On 
          
    RewriteCond %{QUERY_STRING} ^public/(.+)$  
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f  
    RewriteRule ^(.*)$  public/%1 [NC,R,L]

    RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
```

# phpmyadmin修改sql

https://hackmd.io/kORpcygzQuKxr0AqMUu-_g


# Mysql

## 大量資料減少查詢時間
原本
```sql=
select * 
from `news` 
where (`enabled`=1 or web_data_active=1) and 
length(`subject`)>10 and 
`newsid` != '20231221A001001' and 
(`end_datetime` is null or `end_datetime` > '2024-01-09 10:39:00') and 
`start_datetime` < '2023-12-21 13:49:01' and 
`start_datetime` < '2024-01-09 10:39:00' and 
`news`.`deleted_at` is null 
order by start_datetime desc,newsid asc 
LIMIT 0,1;
```
修改後
```sql=
SELECT `news`.* 
FROM `news` 
JOIN ( 
    SELECT `newsid` 
    FROM `news` 
    WHERE (`enabled` = 1 OR `web_data_active` = 1) AND 
    LENGTH(`subject`) > 10 AND 
    `newsid` != '20231221A001001' AND 
    (`end_datetime` IS NULL OR `end_datetime` > '2024-01-09 10:39:00') AND 
    `start_datetime` < '2023-12-21 13:49:01' AND 
    `start_datetime` < '2024-01-09 10:39:00' AND 
    `news`.`deleted_at` IS NULL 
    ORDER BY `start_datetime` DESC, `newsid` ASC LIMIT 0, 1 ) AS subquery
    ON `news`.`newsid` = subquery.`newsid`;
```
ORM方式
第一種
```php=
<?php
$result = News::join(
    DB::raw('(SELECT newsid
              FROM news
              WHERE (enabled = 1 OR web_data_active = 1)
                  AND LENGTH(subject) > 10
                  AND newsid != "20231221A001001"
                  AND (end_datetime IS NULL OR end_datetime > "2024-01-09 10:39:00")
                  AND start_datetime < "2023-12-21 13:49:01"
                  AND start_datetime < "2024-01-09 10:39:00"
                  AND deleted_at IS NULL
              ORDER BY start_datetime DESC, newsid ASC
              LIMIT 0, 1) as subquery'),
    function ($join) {
        $join->on('news.newsid', '=', 'subquery.newsid');
    }
)
->select('news.*')
->get();
```
第二種
```php=
<?php
$subquery = News::select('newsid')
    ->where(function ($query) {
        $query->where('enabled', 1)
            ->orWhere('web_data_active', 1);
    })
    ->where('subject', '>', 10)
    ->where('newsid', '!=', '20231221A001001')
    ->where(function ($query) {
        $query->whereNull('end_datetime')
            ->orWhere('end_datetime', '>', '2024-01-09 10:39:00');
    })
    ->where('start_datetime', '<', '2023-12-21 13:49:01')
    ->where('start_datetime', '<', '2024-01-09 10:39:00')
    ->whereNull('deleted_at')
    ->orderBy('start_datetime', 'desc')
    ->orderBy('newsid', 'asc')
    ->limit(1);

$result = News::whereIn('newsid', $subquery)
    ->get();

```
原理join的是原本where得資料但只搜尋newsid可以減少查詢時間後再去on id去等於抓取那比資料

## 大量減少查詢時間2(先抓id在where)

### 原生語法
```sql=
select * from `news` where `newsid` = (
    select `newsid` from `news` 
    where (`enabled` = ? or `web_data_active` = ?) and 
    length(`subject`)>10 and 
    `newsid` != ? and 
    (`end_datetime` is null or `end_datetime` > ?) and 
    `start_datetime` < ? and `start_datetime` >= ? and 
    `start_datetime` < ? and `deleted_at` is null 
    order by `start_datetime` desc 
    limit 1
) 
and `news`.`deleted_at` is null
```

### ORM

```php=
<?php
$news = News::where('newsid', function ($query) use($newsid,$start_datetime,$orm) {
                    $query->select('newsid')
                    ->from('news')
                    ->where(function ($subquery) {
                        $subquery->where('enabled', 1)
                        ->orWhere('web_data_active', 1);
                    })
                    ->whereRaw('length(`subject`)>10')
                    ->where('newsid', '!=', $newsid)
                    ->where(function ($subquery) {
                        $subquery->whereNull('end_datetime')
                            ->orWhere('end_datetime', '>', date("Y-m-d H:i:00"));
                    })
                    ->where('start_datetime', '<', date("Y-m-d H:i:00"))
                    ->where('start_datetime', '>=', date("Y-m-d",strtotime("-2 day", strtotime($start_datetime))))
                    ->where('start_datetime', '<', $start_datetime)
                    ->whereNull('deleted_at')
                    ->orderByDesc('start_datetime')
                    ->first();
                });
```

### 在php用if的orm

```php=
<?php

$conditions["whereraw"][] = "(`enabled`=1 or web_data_active=1) and length(`subject`)>10";
                $conditions["where"][] = array("start_datetime","<=",date("Y-m-d H:i:00"));
                $conditions["orwhere"][] = array(
                                                array("end_datetime","=",null),
                                                array("end_datetime",">",date("Y-m-d H:i:00")),
                                            );
$conditions["wherehas"]["tags"] = array("id","=",$tag);
                $news = News::select('news.*')
                                    ->whereHas('tags', function ($query) use ($tag) {
                    $query->where('id', $tag);
                })
                ->joinSub(function ($query) use ($conditions) {
                    $query->select('newsid')
                        ->from('news');
                    foreach($conditions as $sql_where => $sql_data){
                        switch ($sql_where){
                            case "where":
                                foreach ($sql_data as $item) {
                                    $query->where($item[0],$item[1],$item[2]);
                                }
                                break;
                            case "whereraw":
                                foreach ($sql_data as $raw) {
                                    $query->whereRaw($raw);
                                }
                                break;
                            case "orwhere":
                                foreach ($sql_data as $item) {
                                    $query->where(function ($query)use($item) {
                                        foreach($item as $sql_key=>$array){
                                            if($sql_key==0){
                                                $query->where($array[0],$array[1],$array[2]);
                                            }else{
                                                $query->orwhere($array[0],$array[1],$array[2]);
                                            }
                                        }
                                    });
                                }
                                break;
                        }
                    }
                    $query->orderByDesc('start_datetime');
                    $query->limit(12);
                }, 'subquery', function ($join) {
                    $join->on('news.newsid', '=', 'subquery.newsid');
                });
```


## 搜尋重覆資料且多筆資料只留id最小資料其他列出來(可用於刪除)

```sql=
SELECT a.*
FROM stock_daily_price a
JOIN (
    SELECT
        stock_id,
        stock_name,
        closing_price,
        stock_price_change,
        opening_price,
        highest_price,
        lowest_price,
        MIN(id) AS min_id
    FROM stock_daily_price
    WHERE updated_at LIKE '2023-12-01%'
    GROUP BY
        stock_id,
        stock_name,
        closing_price,
        stock_price_change,
        opening_price,
        highest_price,
        lowest_price
    HAVING COUNT(*) >= 2
) b ON
    a.stock_id = b.stock_id
    AND a.stock_name = b.stock_name
    AND a.closing_price = b.closing_price
    AND a.stock_price_change = b.stock_price_change
    AND a.opening_price = b.opening_price
    AND a.highest_price = b.highest_price
    AND a.lowest_price = b.lowest_price
    AND a.id != b.min_id
WHERE a.updated_at LIKE '2023-12-01%';
```

## 搜尋重覆資料兩個相同條件

```sql=
SELECT *
FROM daily_tvstock
WHERE updated_at LIKE '2023-12-01%'
GROUP BY stock_id,hit_count  -- 列出所有你想要檢查重複的欄位
HAVING COUNT(*) >= 2;
```

## 刪除相同重覆資料兩筆兩個相同條件且留id小的
```sql=
DELETE t1
FROM daily_tvstock t1
JOIN daily_tvstock t2 
ON t1.stock_id = t2.stock_id 
AND t1.hit_count = t2.hit_count
AND t1.id > t2.id
WHERE t1.updated_at LIKE '2023-12-01%'
AND t2.updated_at LIKE '2023-12-01%';
```

## CONCAT_WS 用來將多個字串值連接起來，並使用指定的分隔符進行分隔
```sql=
where用
concat_ws(\'\', name,city, sub_area, address) like "%'.$search_txt.'%"  '

selesect用

CONCAT_WS(separator, string1, string2, string3, ...)
1. separator：分隔符，用來將不同的字串值分隔開來。
2. string1, string2, string3, ...：要連接的字串值。

SELECT CONCAT_WS(', ', first_name, last_name) AS full_name
FROM users;

+--------------+
| full_name    |
+--------------+
| John, Doe    |
| Jane, Smith  |
| Alice, Brown |
+--------------+

```

### in,exists 适用于左边大表，右边小表。
https://www.zhihu.com/question/388019803/answer/3167172828

假如有order和user兩張表，其中order表有10000條數據，而user表有100條數據。
這時如果想查一下，所有有效的用戶下過的訂單清單。 可以使用in關鍵字實作：
select * from order
where user_id in (select id from user where status=1)
也可以使用exists關鍵字實作：
select * from order
where exists (select 1 from user where order.user_id = user.id and status=1)
前面提到的這種業務場景，使用in關鍵字去實現業務需求，更加適合。
為什麼呢？

因為如果sql語句中包含了in關鍵字，則它會優先執行in裡面的子查詢語句，
然後再執行in外面的語句。 如果in裡面的資料量很少，作為條件查詢速度更快。

而如果sql語句中包含了exists關鍵字，它優先執行exists左邊的語句（即主查詢語句）。
然後把它當作條件，去跟右邊的語句配對。 如果符合上，則可以查詢出資料。
如果匹配不上，資料就被過濾掉了。
這個需求中，order表有10000條數據，而user表有100個數據。
order表是大表，user表是小表。 如果order表在左邊，用in關鍵字表現較好。

exists 適用於左邊小表，右邊大表。
不管是用in，還是exists關鍵字，其核心想法都是用小表驅動大表。

# javascript

## <script defer src="{{ asset(mix('js/page/search_model.js'), true) }}" ></script>>

script defer:
這是一個 HTML <script> 標籤的屬性，defer 屬性的作用是告訴瀏覽器在解析完 HTML 文件後才執行這個 JavaScript 檔案，這樣可以確保 DOM（文件物件模型）已經建立完畢。

src="{{ asset(mix('js/page/search_model.js'), true) }}"：
這部分是試圖動態生成 JavaScript 檔案的 URL。在 Laravel 框架中，asset 函式用於生成資源的 URL，而 mix 函式則通常用於為資源加上版本號（cache busting），以防止瀏覽器緩存舊版本。最後的 true 參數表示生成絕對 URL。

## history.replaceState 修改歷史標籤，但不會將瀏覽器重定向到新的URL
```javascript=
history.replaceState(stateObj, title, url);
```

這個方法用於修改歷史標籤，但不會將瀏覽器重定向到新的URL。

stateObj: 表示一個JavaScript對象，與新的狀態有關，可以是null。
title: 用於新的狀態的標題，現代瀏覽器通常忽略這個參數，因此可以傳入空字符串。
url: 新的URL。
在你的代碼中，你使用了 history.replaceState 來替換當前的URL，並且將URL設置為 {{url("/program-post/img")}} + "/" + extractedString。這樣的代碼片段看起來像是在修改URL，使其包含 "/program-post/img" 和 extractedString。

請確保 {{url("/program-post/img")}} 和 extractedString 的值都是正確的並且不包含空格或非法字符，以確保生成的URL是有效的。

## 擷取字串

```javascript=
var img_url_now = this.slides[index].dataset.url;
          // 從後面尋找第一個斜線的索引
          var lastIndex = img_url_now.lastIndexOf("/");
          var dotIndex = img_url_now.lastIndexOf(".");
          // 擷取字串
          var extractedString = img_url_now.substring(lastIndex+1,dotIndex);
```

# jQuery

## 增加屬性attr
$('#search_choose_form').attr('action',search_route);
## 移除屬性removeAttr
$('#search_input').removeAttr('name');
## 增加css
$('#search_input').css('display','none');

## 從某個class底下去尋找index
```javascript=
$('.schedule').eq($('#week').val()-1).find('tbody tr').eq(index).addClass('highlight-row');
//查看name在第幾個index
$(document).on("change", 'input[name^="images"]', function (e) {   
var inputIndex = $('input[name^="images"]').index(this);
var width = $("#rich_menu_select_block").find(".div_block:eq("+inputIndex+")").css('width');
var height = $("#rich_menu_select_block").find(".div_block:eq("+inputIndex+")").css('height')
});
```
## jQuery Laravel url 用在juqery
```javascript=
$("#submit").attr("data-refresh-url","{!! url('/liveendyoutubeschedule?week="+week+"') !!}");
```
## jQuery 程式觸發change事件
```javascript=
$('#data_key').val(key).trigger('change');
//觸發change事件
```
## jQuery name onchange
```javascript=
$('select[name^="rich_menu_type"]').change(function() {
    var selectedIndex = $('select[name^="rich_menu_type"]').index(this);
    console.log("Index: " + selectedIndex);
});
```
## jQuery id onchange
```javascript=
 $('#data_key').on('change', function() {
     $(this).find(":selected").val();
     $.each( p_data["data"],function(key,array){
                $('#list_id').append($('<option>', {value:array["id"], text:array["title"]}));
            });
 });
```

## jQuery
最後一行為清除url的get資料
```javascript=
        localStorage.removeItem('countdownSeconds');
        localStorage.setItem('countdownSeconds', 30);
        history.replaceState({}, document.title, window.location.pathname);
```

## jQuery input file 修改檔案
```htmlembedded=
<input type="file" class="form-control" id="multiple_img" name="multiple_img[]" multiple>
<script>
    $(document).ready(function() {
        $('#multiple_img').change(function() {
            var files = $('#multiple_img')[0].files;
            var dataTransfer = new DataTransfer();
            if (files.length > 0) {
                var newFiles = [];
                for (var i = 0; i < files.length; i++) {
                    if (i !== 0) {
                        dataTransfer.items.add(files[i]);
                    }
                }
            }
            // $('#multiple_img_copy')[0].files = newFiles;
            $('#multiple_img')[0].files = dataTransfer.files;
        });
    });
</script>
```

## jQuery input file兩邊同時傳
```javascript=
$('.container').on('click', '.select-save-block-image-trigger', function() {
            var triggerType = $(this).data('trigger-type');
            // 執行相關的邏輯，基於 triggerType（A 或 B）
            if (triggerType === 'A') {
                var fileInputs = $("input[name='images_a[]']");
                var fileTemps = $("input[name='images_temp_a[]']");
            } else if (triggerType === 'B') {
                var fileInputs = $("input[name='images_b[]']");
                var fileTemps = $("input[name='images_temp_b[]']")
            }
            console.log(fileInputs);
            fileInputs.each(function(index, element) {
                var fileValue = $(this).val();
                if (fileValue) {
                    var fileInput = element; // 获取原生 DOM 元素
                    var file = fileInput.files[index]; // 获取文件
                    var fileList = new DataTransfer();
                    fileList.items.add(file);
                    // 设置到相应的目标文件输入元素（例如，fileTemps）
                    fileTemps.eq(index).prop('files', fileList.files);
                }
            });


        });
```
## jQuery file預覽
```html=
<input type="file" class="form-control" id="multiple_img" name="multiple_img[]" multiple>
<table class="table table-bordered" id="imagePreviewContainer">
    <tbody>
    <tbody>
</table>
```
```javascript=
<script>

    $(document).ready(function() {
        $('#multiple_img').change(function() {
            readURL(this);
        });
    });
    function readURL(input) {
        $('#imagePreviewContainer tbody').empty();
        var table = `
                    <tr class="active">
                        <th style="width:10px;">排序</th>
                        <th style="width:40px;">圖片</th>    
                    </tr>`;
        if (input.files && input.files.length > 0) {

            for (let i = 0; i < input.files.length; i++) {

                const reader = new FileReader();
                // reader.readAsDataURL(input.files[i]);
                const objectURL = URL.createObjectURL(input.files[i]);
                var sort = i+1;
                table += `
                    <tr id="item-${i}">
                        <td>${sort}</td>
                        <td>
                            <div><img src = "${objectURL}" style="width:200px;"></img></div>
                        </td>
                    </tr>`;
            }
            $('#imagePreviewContainer tbody').append(table);


        }
    }
</script>
```
## jQuery input <0正規式
```javascript=
 $("#skip_time").on("input", function() {
                // 获取输入框的值
                var inputValue = $(this).val();
                // 使用正则表达式检查是否为负数
                var regex = /^-\d+(\.\d+)?$/;
                if (regex.test(inputValue)) {
                    // 如果是负数，将其改成0
                    $(this).val("0");
                }
            });
```

## jQuery checkbox 多個name使用
```javascript=
//if簡寫
id ? id = true : id = false;
//日期差
var date = new Date(checkin_date).getTime()-new Date(checkout_date).getTime();
var days = Math.abs(date)/(24*3600*1000);
// js 計算月日
var start_date = new Date(checkin_date).getTime()-new Date(order_date).getTime();
var start_day = Math.abs(start_date)/(24*3600*1000);
//指定日期加幾天
date = new Date (new Date(checkin_date).setDate(new Date(checkin_date).getDate() + i));
getYearMonthDay(date,'/');
//將年月日放進來進行字串整理
function getYearMonthDay(date,symbol){
        year = date.getFullYear()
        month = date.getMonth()+1;
        day = date.getDate()
        if (day < 10) {
            day = '0' + day;
        }
        if (month < 10) {
            month = '0' + month;
        }
        r
//判斷有沒有進行勾選
$('#' + id).is(":checked")

jQuery <1.9
$('#inputId').attr('readonly', true);
jQuery 1.9+
$('#inputId').prop('readonly', true);
存資料
$('#checkin_district').val( 123 );
// 多個id或name用相同的event事件用法
$(document).on('input', '#order_name, #order_phone_number, #order_email', function() {
    });
    
    //取得data-key的資料 多個name使用
    <select name="time_of_room" data-key="{{ $key }}" style="width: 180px">
        $(document).on('change', 'select[name="time_of_room"]', function(key,value) {
            key = $(this).attr("data-key");
            $('#time_of_room_'+key).prop('checked', true);
        });
    //name checked
    $('input[name="time_of_meal_'+key+'"]')[key].checked = true
    //選下拉選單第一個
    $('select[name="time_of_restaurant"]').val($('select[name="time_of_restaurant"] option:first').val());
```
## jQuery 連點submit問題

```php=
<input type="hidden" class="form-control" id="lastClickTime" name="lastClickTime" value="0">
<button type="submit" class="btn btn-info pull-right _submit_" data-function="form_check();" data-form-id="form" data-refresh-url="{{ url('/analyster/video') }}">提交</button>

<script type="text/javascript">
function form_check() {
    var lastClickTime = $("#lastClickTime").val();
    var currentTime = new Date().getTime();
    var clickInterval = 2000; // 1秒
    $("#lastClickTime").val(new Date().getTime());
    if (currentTime - lastClickTime > clickInterval) {
        if (!analyster_yt_different()) {
            return false;
        }
        return true;
    }
    return false;
}
</script>

```

## jQuery 按鈕開關

```php=
    <style>
            .tr-odd{background-color: #ccc;}
            .tr-hover{background-color: #0fa;}
            .btn-switch {
                color: whitesmoke;
                width: 6rem;
                height: 2.5rem;
                margin:.1rem;
                padding: .1rem;
                background: #555;
                border: 2px solid #ccc;
                border-radius: 2rem;
                transition: .3s;
                box-shadow: inset 1px 1px 5px #333, .1rem .2rem .5rem #999;
            }
            .btn-switch-ball {
                color: whitesmoke;
                width: 2rem;
                height: 2rem;
                background: #777;
                border: inset .1rem rgba(153, 153, 153, 0.43);
                border-radius: 50%;
                box-sizing: border-box;
                cursor: pointer;
                transition: .3s;
                box-shadow: 1px 1px 5px #000;
            }
            .on .btn-switch {
                background: #777;
            }
            .on .btn-switch-ball {
                background: #999;
                /* outset .1rem rgba(153, 153, 153, 0.43) */
                box-shadow: -1px 1px 5px #000;
                margin-left: calc(100% - 2rem);
            }
    </style>
@foreach ($pager as $item)
    @if($item['enable']==1)  
        <button class="btn-switch">
        <div class="btn-switch-ball" data-id="{{ $item['id'] }}">開</div>
        </button>
    @else 
        <button class="btn-switch on">
            <div class="btn-switch-ball" data-id="{{ $item['id'] }}">關</div>
        </button>  
    @endif
@endforeach
<script>    
    $(document).ready(function () {
        $('.btn-switch-ball').on('click', function () {
            var $this = $(this);
            var enable = 0;
            var message = "";
            var dataId = $this.data('id');
            if ($this.parent().hasClass('on')) {
                message = "確定要進行啟用嗎?";
                enable = 1;
            } else {
                message = "確定要進行關閉嗎?";
                enable = 0;
            }
            if(confirm(message)){
                $this.parent().toggleClass('on');
                if(enable==1){
                    $this.html("開");
                }else{
                    $this.html("關");
                }
                $.ajax({
                    data: {
                        id: dataId,
                        enable: enable,
                    },
                    type: 'POST',
                    dataType: 'JSON',
                    url: "{{url('/analyster/analyster_enable')}}",
                    success: function (response) {
                        console.log(response);
                        alert(response.msg);

                    },error: function(response) {
                        alert(response.msg);
                    }
                });   
            }
            
                    
        });
    });  

    $('tr').hover(function() {
        $(this).addClass('tr-hover');
    }, function() {
        $(this).removeClass('tr-hover');
    });
});
```
ajax打的資料
```php=
<?php
    public function analyster_enable(Request $request){
      $AnalystVideo = AnalystVideo::find($request->id);
      $AnalystVideoGcp = AnalystVideo::on('azure_mysql')->find($request->id);
      switch ($request->enable){
        case 0:
          $AnalystVideo->enable = 0;
          $AnalystVideoGcp->enable = 0;
          break;
        case 1:
          $AnalystVideo->enable = 1;
          $AnalystVideoGcp->enable = 1;
          break;
        default:
          $AnalystVideo->enable = 0;
          $AnalystVideoGcp->enable = 0;
      }
      if(!$AnalystVideo->save()){
        return $this->retJson(503, '啟用修改失敗!');
      }
      $AnalystVideoGcp->save();
      return $this->retJson(200, '操作成功!');
    }
```

## jQuery 抓youtube 的影片長度
```html=
//這一塊用來隱藏iframe youtube的資料，因要抓影片長度資料就會顯示
<div id="player_div" hidden>
    // player會進行刪除後再新增，若不這樣會影響抓資料的問題
    <div id="player"></div>
</div>


<input type="text" class="form-control" name="video_id" id="video_id" value="@if(isset($item)){{ $item->video_id }}@endif">
<input type="hidden" class="form-control" id="video_duration" name="video_duration" value="0">

<button type="button" id="form_button"  class="btn btn-info pull-right">提交</button>
<button type="submit" id="submit"  style="display: none;">

```javascript=
<script src="https://www.youtube.com/iframe_api"></script>
$('#form_button').on('click', function() {
    event.preventDefault(); // 阻止默认的提交行为
    var videoId = extractYouTubeVideoId($('#video_id').val());
    onYouTubeIframeAPIReady(videoId).then(function() {
        onPlayerReady(function(videoDuration) {                       
            // 手动触发表单提交
            $("#submit").click();
        });
    });
});

$(document).ready(function() {
        $('#youtube').on('input', function() {
            $("#player_div").hide();
            $("#player_div").html("");
            $("#video_duration").val(0);
            $("#player_div").append(`<div id="player"></div>`);
            var videoId = extractYouTubeVideoId($('#youtube').val());
            if(videoId){
                onYouTubeIframeAPIReady(videoId);
                setTimeout(function() {
                    console.log($("#video_duration").val());
                    if ($("#video_duration").val() > 0) {
                        console.log('影片有秒數，不進行移除');
                    } else {
                        console.log('影片沒有秒數，移除 iframe');
                        $("#player_div").show();
                        $("#player_div").html("");
                        $("#player_div").append(`<font color="red">影片不存在或未公開!</font>`);
                    }
                }, 1000);
            }
                
        });

        function onYouTubeIframeAPIReady(video_id) {
            return new Promise(function(resolve, reject) {
                player = new YT.Player('player', {
                    height: '360',
                    width: '640',
                    videoId: video_id,
                    events: {
                        'onReady': function() {
                            resolve();
                            onPlayerReady();
                        },
                        'onError': function(errEvent) {
                            console.log(errEvent);
                            var errCode = errEvent.data;
                            if(errCode == 100) {
                                alert('Video Not Found');
                            }
                        }
                    }
                });
            });
        }

        function onPlayerReady() {
            try {
                $("#player_div").show();
                console.log(player);
                var videoDuration = player.getDuration(); // 获取视频长度，单位为秒
                var videoTitle = player.getVideoData().title;
                console.log("视频標題：", videoTitle, "秒");
                console.log("视频長度：", videoDuration, "秒");
                $("#video_duration").val(videoDuration);
                player.pauseVideo(); // 暂停播放
                player.mute(); // 静音
                player.setVolume(0); // 设置音量为 0
            } catch (error) {
                // 在這裡加入錯誤處理的邏輯
                console.error("發生錯誤：", error);
                // 在這裡加入其他處理錯誤的邏輯
                $("#player_div").html(""); // 清空 player_div
            }
        }
        function extractYouTubeVideoId(link) {
            var videoId = '';
            var regex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?.*v=([a-zA-Z0-9_-]{11})/;
            var match = link.match(regex);
            if (match && match[1]) {
                videoId = match[1];
                return videoId;
            } else {
                return link;
            }
        }
    });
```

## jQuery sortable 表格行拖曳

### 第一種圖片上傳或修改時調整順序
subject_item.blade.php
```php=
<?php
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
</head>

<input type="file" class="form-control" id="multiple_img" name="multiple_img[]" multiple>
                                        <input type="hidden" class="form-control" id="tmp_img_sort" name="tmp_img_sort" value="@if(isset($item)){{ $item->multiple_img }}@endif" >

<table class="table table-bordered" id="imagePreviewContainer">
                                <p class="table-remark" @if(!isset($item))style="display: none;"@endif><font color="red">*拖曳圖片即可進行排序，拖曳排序完成後需按「提交」按鈕，才會進行儲存這次的排序內容。</font></p>
                                <tbody>
                                    @if(isset($item->multiple_imgs))
                                    <tr class="active">
                                        <th style="width:50px;">排序</th>
                                        <th>圖片</th>    
                                    </tr>
                                    @foreach ($item->multiple_imgs as $key=>$url)
                                    <tr id="item-{{$key}}" class="draggable">
                                        <td>{{  $key+1 }}</td>
                                        <td>
                                            <div><a target="_blank" href="{{ asset("storage/{$url}?".date('YmdHis')) }}"><img src = "{{ asset("storage/{$url}?".date('YmdHis')) }}" style="width:200px;"></img></a></div>
                                        </td>
                                    </tr>
                                    @endforeach
                                    @endif
                                </tbody>
                            </table>
```
js得部分
```javascirpt=
$(document).ready(function() {
        $('#multiple_img').change(function() {
            
            readURL(this);
            var files = $('#multiple_img')[0].files;
            $("#tmp_img_sort").val("");
            if (files.length > 0) {
                var array = [];
                $.each(files, function(index, file) {
                    var fileName = file.name;
                    array.push(`${fileName}`);
                });
                $('#tmp_img_sort').val(array.join(';'));
            }else{
                $(".multiple-img-remark").show();
            }
        });
        $( "tbody" ).sortable({
            items: 'tr.draggable',
            axis: 'y',
            update: function (event, ui) {
                var serializedString = $(this).sortable('serialize');
                var idArray = serializedString.split('&').map(function(pair) {
                    return pair.split('=')[1];
                });

                // 使用分號拆分成每個鍵值對
                var tmp_img_sort = $('#tmp_img_sort').val().split(';');

                // 使用 map 方法創建包含索引和值的新陣列
                var indexedArray = $.map(tmp_img_sort, function(value, index) {
                    return { index: idArray.indexOf(index.toString()), value: value };
                });

                // 使用 sort 方法，根據每個元素的索引進行排序
                indexedArray.sort(function(a, b) {
                    return a.index - b.index;
                });

                // 提取排序後的值
                var sortedValues = $.map(indexedArray, function(item) {
                    return item.value;
                });
                $("#tmp_img_sort").val(sortedValues.join(';'));              
                
                updateFirstColumn();
            }
        });
        $( "#sortable" ).disableSelection();
)};
// 更新表格的第一列数据
    function updateFirstColumn() {
        // 获取所有行的第一列
        var firstColumnCells = $("tbody tr td:first-child");

        // 遍历排序后的索引，更新第一列的文本
        $("tbody tr").each(function (index, row) {
            var newIndex = $(row).index();
            var newText = newIndex+1; // 你可以根据需要修改这里的逻辑
            
            $(firstColumnCells[index]).text(newText);
            if(index>0){
                var newId = "item-" + (index-1);
                $(row).attr("id", newId);
            }
        });
    }
    $( "#sortable" ).disableSelection();	
    function readURL(input) {
        $(".table-remark").hide();
        $('#imagePreviewContainer tbody').empty();
        var table = `
                    <tr class="active">
                        <th style="width:50px;">排序</th>
                        <th>圖片</th>    
                    </tr>`;
        if (input.files && input.files.length > 0) {
            $(".table-remark").show();
            for (let i = 0; i < input.files.length; i++) {

                const reader = new FileReader();
                // reader.readAsDataURL(input.files[i]);
                const objectURL = URL.createObjectURL(input.files[i]);
                var sort = i+1;
                table += `
                    <tr id="item-${i}" class="draggable">
                        <td>${sort}</td>
                        <td>
                            <div><img src = "${objectURL}" style="width:200px;"></img></div>
                        </td>
                    </tr>`;
            }
            $('#imagePreviewContainer tbody').append(table);
        }
    }
```
SubjectDateController.php
```php=
<?php
$tmp_img_sorts = explode(";",$request->tmp_img_sort);
        $SubjectDate = SubjectDate::find($id);  
        if ($request->hasFile('multiple_img')) {
            $multiple_imgs = array();
            foreach ($request->file('multiple_img') as $key=>$image) {
                $originalName =  $image->getClientOriginalName();
                $extension =  $image->getClientOriginalExtension();
                $size = $image->getSize();
                $mime = $image->getMimeType();
                $filekey = array_search($originalName, $tmp_img_sorts);
                $fileName = $SubjectDate->id .'_img' .($filekey+1) .'.'.$extension;      
                $filePath_name = $image->storeAs(config('project.subject_date_dir'), $fileName, 'public');
                $multiple_imgs[$filekey] = $filePath_name;
                
            }
            asort($multiple_imgs);
            $data["multiple_img"] = implode(";",$multiple_imgs);
            
        }else{
            if($request->input('_method') == 'PUT'){
                $data["multiple_img"] = $request->tmp_img_sort;
            }
        }
```
### 第二種直接拖曳修改sort的部分
subject_list.blade.php
```php=
<?php
<table class="table table-bordered">
                            <tbody>
                                <tr class="active">
                                    <th style="width:50px;">ID</th>
                                    <th style="width:200px;">日期</th>  
                                    <th style="width:400px;">主題</th>  
                                    <th>簡介</th>
                                    <th style="width:200px;">上架日期</th>                               
                                    <th style="width:200px;">操作</th>
                                </tr>
                                @foreach ($data as $item)
                                <tr id="item-{{$item->id}}" class="draggable">
                                    <td>{{  $item->id }}</td>
                                    <td>
                                        <div>{{ $item->date }}</div>
                                    </td>
                                    <td>
                                        <div>{{ $item->subject }}</div>
                                    </td>
                                    <td>
                                        <div><pre>{!! $item->intro !!}</pre></div>
                                    </td>
                                    <td>
                                        <div>{{ $item->start_date }}</div>
                                    </td>
                                    <td>
                                        <!-- <button><a href="{{ url('/subjectdate')}}/{{$item['id']}}/editimgsort">圖片排序</a></button> -->
                                        <a class="btn btn-primary" role="button" href="{{ url('/subjectdate')}}/{{$item->id}}/edit">編輯</a>
                                        <a class="btn btn-secondary _delete_" role="button" href="#" class="_delete_" data-url="{{ url('/subjectdate')}}/{{$item->id}}" data-errmsg="確定要刪除 {{$item->subject}} 嗎？">刪除</a>
                                    </td>
                                </tr>
                                @endforeach
                            </tbody>
                        </table>
```

js的部分
```javascirpt=
$(document).ready(function() {
        

        $( "tbody" ).sortable({
            items: 'tr.draggable',
            axis: 'y',
            update: function (event, ui) {
                var data = $(this).sortable('serialize');
                // POST to server using $.post or $.ajax
                var csrfToken = "{{ csrf_token() }}";
                $.ajax({
                    data: data,
                    type: 'POST',
                    url: '{{url('/subjectdate/group/saveorder')}}',
                    headers: {
                        'X-CSRF-TOKEN': csrfToken
                    },
                    success: function (response) {
                        // updateFirstColumn();
                        location.href = location.href;
                    }
                });
            }
        });
        $( "#sortable" ).disableSelection();
    });
```

SubjectDateController.php
```php=
<?php
    public function saveorder(Request $request){
        // $data = SubjectDate::find($request->id);
        // $multiple_imgs = explode(";",$data->multiple_img);
        // // 重新排列数组
        // $sortedArray = array_map(function ($index) use ($multiple_imgs) {
        //     return $multiple_imgs[$index];
        // }, $request->item);
        // SubjectDate::where('id', $request->id)->update(['multiple_img' => implode(";",$sortedArray)]);
        // dd($request->item);
        $i = 0;
        foreach ($request->item as $index => $id) {
          $item = SubjectDate::where('id', $id)->first();
          $item->update(array('sort'=>$index));
          $i++;
        }
    }
```

## jQuery 時間條
```javascript=
    .time-bar {
      width: 500px;
      height: 20px;
      border: 1px solid black;
      position: relative;
    }

    .time-handle {
      width: 0;
      height: 0;
      border-left: 5px solid transparent;
      border-right: 5px solid transparent;
      position: absolute;
      bottom: -12px;
      cursor: pointer;
    }

    .start-handle {
      left: 0;
      border-bottom: 10px solid blue;
    }

    .end-handle {
      right: 0;
      border-bottom: 10px solid blue;
    }

    .time-range {
      position: absolute;
      bottom: 0px;
      height: 20px;
      background-color: green;
      transform-origin: left;
    }

    .time-prohibit {
      position: absolute;
      bottom: 0px;
      height: 20px;
      background-color: red;
      transform-origin: left;
    }

    .time-display {
      margin-top: 10px;
    }
  </style>


                                    <div class="form-group">
                                        <label for="week" class="col-sm-2 control-label">*星期：</label>
                                        <div class="col-sm-3">
                                            <select class="form-control" id="week" name="week">
                                              <option value="1" @if(isset($item) && $item->week==1) selected="selected" @endif>一</option>
                                              <option value="2" @if(isset($item) && $item->week==2) selected="selected" @endif>二</option>
                                              <option value="3" @if(isset($item) && $item->week==3) selected="selected" @endif>三</option>
                                              <option value="4" @if(isset($item) && $item->week==4) selected="selected" @endif>四</option>
                                              <option value="5" @if(isset($item) && $item->week==5) selected="selected" @endif>五</option>
                                              <option value="6" @if(isset($item) && $item->week==6) selected="selected" @endif>六</option>
                                              <option value="7" @if(isset($item) && $item->week==7) selected="selected" @endif>日</option>
                                            </select>
                                        </div>
                                    </div>
                                   
                                     <div class="form-group">
                                        <label for="time" class="col-sm-2 control-label">*節目時段：</label>
                                        <div class="col-sm-10 time-display">
                                            <input type="time" class="form-control" id="start-time" name="time" value="@if(isset($item)){{ $item->time }}@endif" style="width: 140px;display: inline-block;float: none;" >
                                            <p style="display: inline-block;">~</p>
                                            <input type="time" class="form-control" id="end-time" name="end_time" value="@if(isset($item)){{ $item->end_time }}@endif" style="width: 140px; display: inline-block;float: none;" >
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-sm-2">
                                        </div>
                                        <div class="col-sm-10">
                                        <div class="time-bar">
                                            <div class="time-handle start-handle"></div>
                                            <div class="time-handle end-handle"></div>
                                            <div class="time-range"></div>
                                            <!-- <div class="time-prohibit"></div> -->
                                        </div>
                                        </div>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>
<script>
        var item = {!! isset($item) ? json_encode($item) : "false" !!};
        var week_times = {!! $week_times !!};


        $(document).ready(function() {
            var barWidth = $('.time-bar').width();
            var minuteInterval = barWidth / (23 * 60 + 59); // Calculate the width of one minute
            updateTimeBar();

            $('#week').on('change', function() {
                week = $(this).find(":selected").val();
                updateTimeBar();
            });
            // Update the time display based on the handle position
            function updateTimeDisplay(handle, timeField) {
                var handlePosition = handle.position().left;
                var minutes = Math.round(handlePosition / minuteInterval);
                var hours = Math.floor(minutes / 60);
                minutes = minutes % 60;
                var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
                timeField.val(timeString);
            }

            function getHandlePosition(timeString){
                var hours = parseInt(timeString.split(':')[0]);
                var minutes = parseInt(timeString.split(':')[1]);
                var totalMinutes = hours * 60 + minutes;
                return totalMinutes * minuteInterval;
            }

            function updateTimeBar(){
                $(".time-prohibit").remove();
                $.each(week_times,function(key,array){
                    if (array["week"]==$('#week').val()){
                        var startTimeInterval = getHandlePosition(array["time"]);
                        var endTimeInterval = getHandlePosition(array["end_time"]);
                        var left_int =startTimeInterval +5 ;
                        var width_int= endTimeInterval - startTimeInterval;
                        var $newDiv = `<div class="time-prohibit" style="left:${left_int}px;width:${width_int}px;"></div>`;  // creates a div element
                        $('.time-bar').append($newDiv);
                    }
                });
            }
            // Update the handle position based on the time value
            function updateHandlePosition(handle, timeField) {
                var handlePosition = getHandlePosition(timeField.val());
                handle.css('left', handlePosition + 'px');
                $('.time-range').css('left', ($('.start-handle').position().left + 5) + 'px');
                $('.time-range').css('width', ($('.end-handle').position().left - $('.start-handle').position().left) + 'px');
            }

            // Initialize the handle positions and time display
            updateHandlePosition($('.start-handle'), $('#start-time'));
            updateHandlePosition($('.end-handle'), $('#end-time'));
            updateTimeDisplay($('.start-handle'), $('#start-time'));
            updateTimeDisplay($('.end-handle'), $('#end-time'));

            // Handle dragging of the start handle
            $('.start-handle').draggable({
                axis: 'x',
                containment: 'parent',
                drag: function() {
                updateTimeDisplay($(this), $('#start-time'));
                updateHandlePosition($('.start-handle'), $('#start-time'));
                },
                stop: function() {
                updateTimeDisplay($(this), $('#start-time'));
                }
            });

            // Handle dragging of the end handle
            $('.end-handle').draggable({
                axis: 'x',
                containment: 'parent',
                drag: function() {
                updateTimeDisplay($(this), $('#end-time'));
                updateHandlePosition($('.end-handle'), $('#end-time'));
                },
                stop: function() {
                updateTimeDisplay($(this), $('#end-time'));
                }
            });

            // Update start time field when its value changes
            $('#start-time').on('input', function() {
                updateHandlePosition($('.start-handle'), $(this));
            });

            // Update end time field when its value changes
            $('#end-time').on('input', function() {
                updateHandlePosition($('.end-handle'), $(this));
            });
        });
        


    </script>

```
## jQuery遇到的問題

### jQuery submit onclick 事件觸發資料會直接送
這邊可以先利用button做onclick去觸發submit然後把submit隱藏起來就好
```php=
<button type="button" id="form_button"  class="btn btn-info pull-right">提交</button>
<button type="submit" id="submit"  style="display: none;">

$('#form_button').on('click', function() {                    
    // 手动触发表单提交
    $("#submit").click();
});

```

### jQuery Ajax送到controller的update 空值 create有資料
:::danger
錯誤訊息
Symfony\\Component\\HttpKernel\\Exception\\HttpException 
CSRF token mismatch.
:::
:::danger
因Restful API修改用put但實則上ajax type只接受get和post
如果是要修改就要先type=post然後data裡面在上面_method=put就即可解決
:::

```php=
<?php
@if($method=="store")
    <form id="form" action="{{ url('/subjectdate') }}"  enctype="multipart/form-data">
@else
    <form id="form" action="{{ url('/subjectdate/'.$item->id) }}"  enctype="multipart/form-data">
    @method('PUT')
@endif
@csrf
<input type="submit" class="btn btn-info" value="提交">
```

```javascript=
$(document).ready(function() {
$('#form').on('submit', function(e) {
                e.preventDefault(); // 阻止表单默认提交行为
                // 使用 .serializeArray() 获取表单字段
                var formData = $(this).serializeArray();
                form = $('#form')[0];
                var formData = new FormData(form); // 获取表单字段
                var formAction = $(this).attr('action');
                var type = "POST";
                // 发送 AJAX 请求
                $.ajax({
                    type: type,
                    data: formData,
                    url: formAction, // 替换为实际的验证路由
                    processData: false,
                    contentType: false,
                    success: function(response) {
                        // 验证成功，执行相关操作
                        if(response.status=="success"){
                            Swal.fire({
                                title: '成功',
                                text: response.message,
                                icon: 'success',
                                confirmButtonText: '確認',
                                timer: 2000, // 設定計時器為 2000 毫秒（2 秒）
                                showConfirmButton: false // 隱藏確認按鈕
                            }).then((result) => {
                                    // 使用 JavaScript 重新導向到新頁面
                                    location.reload();
                            });
                        }
                        console.log(response);
                    },
                    error: function(xhr) {
                        if (xhr.status === 422) {
                            // 验证失败，显示错误消息
                            var errors = xhr.responseJSON;
                            var errorContainer = $('#errorContainer');
                            errorContainer.find('ul').empty(); // 清空之前的错误消息
                            // 构建错误消息并显示
                            $.each(errors.errors, function(key, value) {
                                errorContainer.find('ul').append('<li>' + value + '</li>');
                            });

                            errorContainer.show();
                        } else {
                            // 处理其他错误
                            console.log('An error occurred');
                        }
                    }
                });
            });
});
```
### jQuery 事件沒反應換成如下
```javascript=
$(document).on("change", 'select[name^="rich_menu_type"]', function () {   
    console.log(this);
});
```

# AJAX

## ajax包function return data
記得PHP 要將資料轉成JSON
json_encode()
```javascript=
function List(id){
    var res = query();
    console.log(res);
    switch(res.status) {
        case 'success':
            alert(msg);
            return false;
            break;
        case 'fail':
            alert(res.msg);
            return false;
            break;
        default:
            alert('查詢空房失敗');
            return false;
    }
}
{{-- (done)查詢 --}}
function SearchRoomquery() {
    var data = '';
    $.ajax({
        url: "{{ route('Front::Search') }}",
        type: 'GET',
        headers: {'X-CSRF-TOKEN': '{{ csrf_token() }}'},
        async: false,
        dataType: 'JSON',
        data: {
            checkin_date: $('#checkin_date').val(),
            checkout_date: $('#checkout_date').val()
            $('#order_data').serialize(),
        },
        success: function (e) {
            data = e;
        }, error: function(xhr, type) {
            // do nothing
        }
    });
    return data;
}
#jQuery onclick(product.edut())
var product = {
    'insert': function() {
        $('#save_button').text('新增');
        $('#save_button').attr("onClick","product.save_insert();");
        $('#order_data')[0].reset();
        $('#checkin_date').attr({"min" : now_date});
        $('#checkout_date').attr({"min" : now_date});
        $('#checkin_date').val(now_date);
    },'edit': function(id) {
            $('h4.modal-title').html('訂單管理');
            OrderListLoad(id);
        $('#product_info').modal('show');
    }
```

## 

```php=

```


# CSS
如果col沒辦法同行有可能套件問題直接加style
```html=
<div style = "display:inline-block">

</div>
```
直接對子層下css
```html=
<style>
.table>tbody>tr>td>a{
            color: #337ab7;
            text-decoration: none;
        }
</style>
```

# npm安裝(webpack.mix.js更改)
```php=
npm install
```
webpack.mix.js 更改
```php=
# 編譯開發資源
npm run dev

# 或者，啟動監視模式，當資源文件更改時自動重新編譯
npm run watch
```
在這兩個指令中，npm run dev 用於編譯開發版本，而 npm run watch 則會啟動監視模式，當檔案更改時自動重新編譯。

運行這些指令之後，Laravel Mix 將根據您的 webpack.mix.js 文件重新編譯前端資源。這樣的更改才會生效，並且生成的檔案將反映您對 webpack.mix.js 文件所做的更改。

請注意，有時可能需要清除瀏覽器緩存，或者在瀏覽器中強制刷新頁面，以確保您看到最新的前端資源。這通常可以通過在瀏覽器中按下 Ctrl + F5（Windows/Linux）或 Cmd + Shift + R（Mac）實現。


