D12 - 掌握 Laravel 控制器,打下邏輯基礎

閱讀時間約 23 分鐘

哈囉,大家好!經過前面的努力,我們已經設計了資料庫結構,建立了模型,並設定了路由。現在,是時候讓我們深入 Laravel 的 控制器(Controller),將一切串聯起來,為我們的個人財務管理系統打造強大的後端 API。

一、建立控制器

首先,我們需要為每個資源建立對應的控制器,包括 UserController、BankAccountController、CategoryController 和 TransactionController。

1. 使用 Artisan 指令建立控制器

打開你的終端機,進入專案目錄,執行以下指令:

php artisan make:controller UserController
php artisan make:controller BankAccountController
php artisan make:controller CategoryController
php artisan make:controller TransactionController

這會在 app/Http/Controllers 目錄下生成四個控制器檔案。

小提醒:善用 Artisan 指令可以大大提高開發效率,讓我們專注於實際的業務邏輯。

二、實作 UserController

讓我們先從 UserController 開始,實作使用者資源的 CRUD 操作。

1. 列出所有使用者(index 方法)

use App\Models\User;

public function index()
{
$users = User::all();
return response()->json($users);
}

這段程式碼相當直觀,我們透過模型取得所有使用者,然後以 JSON 格式回傳。還記得有一次,我需要快速取得所有使用者的列表,這個方法就派上了用場。

2. 建立新使用者(store 方法)

public function store(Request $request)
{
$validatedData = $request->validate([
'username' => 'required|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
]);

$user = User::create([
'username' => $validatedData['username'],
'email' => $validatedData['email'],
'password' => bcrypt($validatedData['password']),
]);

return response()->json($user, 201);
}

在這裡,我們使用 $request->validate() 進行資料驗證,確保使用者輸入的資料符合我們的規則。還記得當初忘記加上驗證,結果收到一堆奇怪的資料,真是讓人頭疼!

3. 顯示特定使用者(show 方法)

public function show($id)
{
$user = User::findOrFail($id);
return response()->json($user);
}

透過 findOrFail,我們可以輕鬆處理找不到資料的情況,避免程式崩潰。這真是 Laravel 體貼的設計啊!

4. 更新使用者資料(update 方法)

public function update(Request $request, $id)
{
$user = User::findOrFail($id);

$validatedData = $request->validate([
'username' => 'sometimes|required|unique:users,username,' . $user->id,
'email' => 'sometimes|required|email|unique:users,email,' . $user->id,
'password' => 'sometimes|required|min:6',
]);

if (isset($validatedData['password'])) {
$validatedData['password'] = bcrypt($validatedData['password']);
}

$user->update($validatedData);

return response()->json($user);
}

這裡特別要注意的是,我們使用了 sometimes 規則,允許部分更新。當初在實作部分更新時,這個規則真是救了我一命!

5. 刪除使用者(destroy 方法)

public function destroy($id)
{
$user = User::findOrFail($id);
$user->delete();
return response()->json(null, 204);
}

刪除資料後,我們回傳 204 No Content,表示請求已成功處理,但沒有內容回傳。

三、實作 BankAccountController

接下來,我們來實作銀行帳戶的控制器方法。

1. 列出所有銀行帳戶(index 方法)

use App\Models\BankAccount;

public function index()
{
$bankAccounts = BankAccount::all();
return response()->json($bankAccounts);
}

2. 建立新銀行帳戶(store 方法)

public function store(Request $request)
{
$validatedData = $request->validate([
'user_id' => 'required|exists:users,id',
'account_name' => 'required|string|max:100',
'account_number' => 'nullable|string|max:50',
'bank_name' => 'nullable|string|max:100',
'balance' => 'nullable|numeric',
]);

$bankAccount = BankAccount::create($validatedData);

return response()->json($bankAccount, 201);
}

在這裡,我們確保了 user_id 必須存在於 users 表中,避免了孤兒資料的產生。

3. 顯示特定銀行帳戶(show 方法)

public function show($id)
{
$bankAccount = BankAccount::findOrFail($id);
return response()->json($bankAccount);
}

4. 更新銀行帳戶(update 方法)

public function update(Request $request, $id)
{
$bankAccount = BankAccount::findOrFail($id);

$validatedData = $request->validate([
'account_name' => 'sometimes|required|string|max:100',
'account_number' => 'nullable|string|max:50',
'bank_name' => 'nullable|string|max:100',
'balance' => 'nullable|numeric',
]);

$bankAccount->update($validatedData);

return response()->json($bankAccount);
}

5. 刪除銀行帳戶(destroy 方法)

public function destroy($id)
{
$bankAccount = BankAccount::findOrFail($id);
$bankAccount->delete();
return response()->json(null, 204);
}

四、實作 CategoryController

現在,我們來為分類資源建立控制器方法。

1. 列出所有分類(index 方法)

use App\Models\Category;

public function index()
{
$categories = Category::all();
return response()->json($categories);
}

2. 建立新分類(store 方法)

public function store(Request $request)
{
$validatedData = $request->validate([
'user_id' => 'required|exists:users,id',
'category_name' => 'required|string|max:100',
'type' => 'required|in:income,expense',
]);

$category = Category::create($validatedData);

return response()->json($category, 201);
}

3. 顯示特定分類(show 方法)

public function show($id)
{
$category = Category::findOrFail($id);
return response()->json($category);
}

4. 更新分類(update 方法)

public function update(Request $request, $id)
{
$category = Category::findOrFail($id);

$validatedData = $request->validate([
'category_name' => 'sometimes|required|string|max:100',
'type' => 'sometimes|required|in:income,expense',
]);

$category->update($validatedData);

return response()->json($category);
}

5. 刪除分類(destroy 方法)

public function destroy($id)
{
$category = Category::findOrFail($id);
$category->delete();
return response()->json(null, 204);
}

五、實作 TransactionController

最後,我們來實作交易資源的控制器。

1. 列出所有交易(index 方法)

use App\Models\Transaction;

public function index()
{
$transactions = Transaction::all();
return response()->json($transactions);
}

2. 建立新交易(store 方法)

public function store(Request $request)
{
$validatedData = $request->validate([
'user_id' => 'required|exists:users,id',
'bank_account_id' => 'required|exists:bank_accounts,id',
'category_id' => 'required|exists:categories,id',
'type' => 'required|in:income,expense',
'amount' => 'required|numeric',
'transaction_date' => 'required|date',
'description' => 'nullable|string|max:255',
]);

$transaction = Transaction::create($validatedData);

return response()->json($transaction, 201);
}

3. 顯示特定交易(show 方法)

public function show($id)
{
$transaction = Transaction::findOrFail($id);
return response()->json($transaction);
}

4. 更新交易(update 方法)

public function update(Request $request, $id)
{
$transaction = Transaction::findOrFail($id);

$validatedData = $request->validate([
'bank_account_id' => 'sometimes|required|exists:bank_accounts,id',
'category_id' => 'sometimes|required|exists:categories,id',
'type' => 'sometimes|required|in:income,expense',
'amount' => 'sometimes|required|numeric',
'transaction_date' => 'sometimes|required|date',
'description' => 'nullable|string|max:255',
]);

$transaction->update($validatedData);

return response()->json($transaction);
}

5. 刪除交易(destroy 方法)

public function destroy($id)
{
$transaction = Transaction::findOrFail($id);
$transaction->delete();
return response()->json(null, 204);
}

六、資料驗證與錯誤處理

在每個控制器中,我們都使用了 $request->validate() 來確保接收到的資料符合我們的預期。這不僅提升了應用程式的健全性,也讓我們更放心地處理資料。

自訂錯誤訊息

如果你想要讓錯誤訊息更貼近使用者,可以這樣做:

$validatedData = $request->validate([
'email' => 'required|email',
], [
'email.required' => '電子郵件是必填的喔!',
'email.email' => '請輸入有效的電子郵件地址。',
]);

我記得有一次,我的客戶反應說錯誤訊息太生硬了,透過自訂錯誤訊息,使用者體驗得到了明顯的改善。

七、與模型的互動

透過控制器,我們可以輕鬆地與模型進行互動,實現各種資料操作。

取得關聯資料

例如,要取得使用者的所有交易紀錄:

public function getUserTransactions($userId)
{
$user = User::findOrFail($userId);
$transactions = $user->transactions;
return response()->json($transactions);
}

這樣的寫法是不是很直觀?Laravel 的 Eloquent ORM 真是讓人愛不釋手!

八、未來的重構與優化

到目前為止,我們的控制器已經能夠正常運作,實現基本的 CRUD 功能。然而,你可能會發現,程式碼中有一些重複的部分,例如資料驗證和錯誤處理。而且,業務邏輯直接寫在控制器中,可能會讓控制器變得臃腫。

這其實很正常,因為我們現在的目標是先讓整個應用程式跑起來,建立一個基本的功能底子。之後,我們會進行 重構,將重複的程式碼抽取出來,優化我們的架構。


為什麼要先寫初版程式碼?

  • 快速迭代:先有一個能跑的版本,可以讓我們更快地看到成果,鼓勵自己繼續前進。
  • 了解需求:透過初版,我們可以更清楚地了解哪些部分需要優化,哪些功能可能需要調整。
  • 避免過度設計:過早優化可能會導致不必要的複雜度,影響開發進度。

未來的重構方向

  • 使用 Form Request:將資料驗證移到專門的類別中,讓控制器更加乾淨。
  • 建立服務層(Service Layer):將業務邏輯從控制器中抽離,提升程式碼的可維護性和可測試性。
  • 實作資源(Resource)和資源集合(Resource Collection):統一 API 回應格式,提升前後端協作效率。

還記得在很久以前剛學Laravel時,一開始也是直接在控制器中寫業務邏輯。後來隨著功能越來越多,程式碼變得難以維護。透過重構,我們將邏輯拆分到不同的類別中,整個專案的結構變得清晰許多,開發效率也大大提升。

九、個人經驗分享

回顧整個開發過程,我深刻體會到控制器在 Laravel 開發中的重要性。它就像是應用程式的指揮官,負責調度各種資源,處理業務邏輯。

然而,直接在控制器中編寫所有邏輯,可能會導致程式碼難以維護。這就是為什麼我們需要考慮未來的重構。透過將重複的部分抽取出來,並將業務邏輯移到適當的位置,我們可以讓程式碼更加乾淨、可讀。我強烈建議大家在開發初期,專注於讓功能先跑起來,之後再進行重構和優化。這樣可以避免過度設計,同時也能在實踐中發現更好的架構方式。

  • 保持控制器精簡:未來可以將複雜的業務邏輯抽取到服務層,讓控制器專注於處理請求和回應。
  • 使用 Form Request:將驗證規則移到專門的類別中,減少控制器中的重複程式碼。
  • 統一錯誤處理:利用全域的例外處理器,統一處理錯誤訊息,提高應用程式的穩定性。
  • 持續重構:定期檢視和優化程式碼,保持良好的程式品質。

小結

今天,我們深入了解了 Laravel 控制器的實作方式,並為我們的 API 建立了完整的控制器邏輯。透過這些內容,我們可以:

  • 有效地組織業務邏輯,提高程式碼的可讀性和維護性。
  • 確保資料的完整性和安全性,提升使用者體驗。
  • 快速地與模型進行互動,實現各種資料操作。
    同時,我們也為未來的重構和優化奠定了基礎。透過先建立一個可運行的版本,我們可以更清楚地了解需要改進的地方,進而優化我們的應用程式。

在獨自開發的過程中就像是一場有趣的旅程,不僅可以提升了技術能力,也可以對框架有了更深的理解。

Next

接下來,我們將進入 API 測試的階段。畢竟,再好的程式碼也需要經過嚴謹的測試才能確保品質。我們會學習如何使用工具來測試 API,並探討一些進階技巧,讓我們的應用程式更加完善。

同時,在未來的文章中,我們會開始進行重構,將目前的程式碼優化,提升整體的架構和效能。讓我們繼續這段充滿挑戰與樂趣的開發之旅吧!相信透過不斷的學習和實踐,我們都能成為更好的開發者。

這是一系列以軟體開發為主題的輕鬆分享,內容涵蓋了技術選擇、開發經驗、實戰應用等多方面的議題。無論是如何在眾多框架中做出選擇,還是如何應對技術轉移的挑戰,作者都用幽默、有趣的對話風格,將複雜的技術問題轉化為易懂的故事。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
本文介紹 Laravel 中的路由設定,討論手動定義路由、群組、前綴和中介層的使用。透過這些技巧,能夠靈活組織 API 結構,提升可讀性和安全性。此外,還探討子資源路由及命名空間等進階技巧,幫助開發者精細控制路由行為。接下來會探討控制器的實作。
本文介紹 Laravel 中的 Model,透過 Eloquent ORM 進行資料庫互動。內容涵蓋 Model 建立、資料表關聯設定,以及利用 Eloquent 特性來優化程式碼。目的是簡化資料庫操作,提高程式碼可讀性與維護性。接下來將開發後端 API,進一步實現系統功能。
這篇文章介紹在 Laravel 中使用 Migration 管理資料庫結構,實現財務管理系統的資料表建立。Migration 提供版本控制、自動化管理和可移植性,方便開發者同步資料庫變更。還介紹了填充測試資料(Seeder)的操作,提高團隊協作效率。
這篇文章介紹了個人財務管理系統的資料庫設計,涵蓋使用者管理、銀行帳戶管理、財務紀錄和分類管理的核心功能。系統需求包括註冊登入、帳戶管理、財務記錄分類和報表生成。設計了四個資料表,並詳細說明其欄位設計和建表語法。透過清晰的表關聯,確保資料一致性和系統擴展性,為後續的 Laravel 開發打下基礎。
好了,經過前幾篇的努力,我們的開發環境已經搭建完成,並進行了初步的測試。一切看起來都很順利,但在正式進入開發之前,我們還有一件重要的事情要做:加入版本控制。 你可能會想:「現在還早吧?我一個人開發,有必要嗎?」但相信我,版本控制就像是遊戲中的存檔點,或者電影裡的多重宇宙時間線,在你需要的時候,
本文指導如何驗收基於 Docker 的開發環境,檢查 Laravel 後端、Nuxt 前端、Nginx 反向代理和 MariaDB 資料庫是否正常運行。透過啟動容器、修改 hosts 檔案、測試各服務的運作,確保整個開發環境穩定。並且提供了常見問題的解決方案,幫助開發者順利驗收環境。
本文介紹 Laravel 中的路由設定,討論手動定義路由、群組、前綴和中介層的使用。透過這些技巧,能夠靈活組織 API 結構,提升可讀性和安全性。此外,還探討子資源路由及命名空間等進階技巧,幫助開發者精細控制路由行為。接下來會探討控制器的實作。
本文介紹 Laravel 中的 Model,透過 Eloquent ORM 進行資料庫互動。內容涵蓋 Model 建立、資料表關聯設定,以及利用 Eloquent 特性來優化程式碼。目的是簡化資料庫操作,提高程式碼可讀性與維護性。接下來將開發後端 API,進一步實現系統功能。
這篇文章介紹在 Laravel 中使用 Migration 管理資料庫結構,實現財務管理系統的資料表建立。Migration 提供版本控制、自動化管理和可移植性,方便開發者同步資料庫變更。還介紹了填充測試資料(Seeder)的操作,提高團隊協作效率。
這篇文章介紹了個人財務管理系統的資料庫設計,涵蓋使用者管理、銀行帳戶管理、財務紀錄和分類管理的核心功能。系統需求包括註冊登入、帳戶管理、財務記錄分類和報表生成。設計了四個資料表,並詳細說明其欄位設計和建表語法。透過清晰的表關聯,確保資料一致性和系統擴展性,為後續的 Laravel 開發打下基礎。
好了,經過前幾篇的努力,我們的開發環境已經搭建完成,並進行了初步的測試。一切看起來都很順利,但在正式進入開發之前,我們還有一件重要的事情要做:加入版本控制。 你可能會想:「現在還早吧?我一個人開發,有必要嗎?」但相信我,版本控制就像是遊戲中的存檔點,或者電影裡的多重宇宙時間線,在你需要的時候,
本文指導如何驗收基於 Docker 的開發環境,檢查 Laravel 後端、Nuxt 前端、Nginx 反向代理和 MariaDB 資料庫是否正常運行。透過啟動容器、修改 hosts 檔案、測試各服務的運作,確保整個開發環境穩定。並且提供了常見問題的解決方案,幫助開發者順利驗收環境。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
Thumbnail
在過去兩年中,我持續運用 Notion 進行個人管理,個人管理的模板也逐漸定型,藉此分享個人管理模板的使用心得。
Thumbnail
在本章節中,我們探討了 PHP 中如何引用和管理套件。學習了如何使用 Composer 來安裝第三方套件,以及如何引用自定義模組。此外,我們還介紹了如何創建和使用自定義套件,並列舉了一些在 PHP 社群中常見且廣泛使用的套件和庫。通過掌握這些知識,開發者可以更有效地管理和利用各種資源。
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
※ 視圖模板 視圖模板(View Templates) 是在 MVC 架構中負責展示數據的 HTML 文件,包含模板語法,用於在渲染時插入實際數據。它們的主要目的是分離數據與展示邏輯,讓代碼更加模塊化和易於維護。 視圖模板設計和使用的核心理念,就是「重複的事情不要重複做、效益最大化、有效利用資源
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
※ 生產者和消費者模式 定義: 生產者和消費者在同一時間內共同存取某一個資料空間。生產者負責生成數據並將其放入共享空間,消費者負責從共享空間中取走數據進行處理。兩者之間互不相干,也不須互相知道對方的存在。 共同存取資料空間:生產者和消費者共享同一個資料空間。這個空間通常是緩衝區或隊列,用於在它
Thumbnail
第一份正職工作 在iot公司擔任後端工程師,一上工就使用先前沒用過的php/laravel,也馬上負責公司產品的架構規劃,先前資料庫只有簡單記載使用者跟使用者的一些設定,很多地方有資料不一致的問題,產品內容還有很多實體的關係沒有被定義進資料庫都是這次改版我要做的事情。 改版納入公司、機器
Thumbnail
題目敘述 題目會給我們一組定義好的界面和需求,要求我們設計一個資料結構,可以滿足平均O(1)的插入元素、刪除元素、隨機取得元素的操作。 RandomizedSet() 類別建構子 bool insert(int val) 插入元素的function界面 bool remove(int val
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
Thumbnail
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
Thumbnail
在過去兩年中,我持續運用 Notion 進行個人管理,個人管理的模板也逐漸定型,藉此分享個人管理模板的使用心得。
Thumbnail
在本章節中,我們探討了 PHP 中如何引用和管理套件。學習了如何使用 Composer 來安裝第三方套件,以及如何引用自定義模組。此外,我們還介紹了如何創建和使用自定義套件,並列舉了一些在 PHP 社群中常見且廣泛使用的套件和庫。通過掌握這些知識,開發者可以更有效地管理和利用各種資源。
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
※ 視圖模板 視圖模板(View Templates) 是在 MVC 架構中負責展示數據的 HTML 文件,包含模板語法,用於在渲染時插入實際數據。它們的主要目的是分離數據與展示邏輯,讓代碼更加模塊化和易於維護。 視圖模板設計和使用的核心理念,就是「重複的事情不要重複做、效益最大化、有效利用資源
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
※ 生產者和消費者模式 定義: 生產者和消費者在同一時間內共同存取某一個資料空間。生產者負責生成數據並將其放入共享空間,消費者負責從共享空間中取走數據進行處理。兩者之間互不相干,也不須互相知道對方的存在。 共同存取資料空間:生產者和消費者共享同一個資料空間。這個空間通常是緩衝區或隊列,用於在它
Thumbnail
第一份正職工作 在iot公司擔任後端工程師,一上工就使用先前沒用過的php/laravel,也馬上負責公司產品的架構規劃,先前資料庫只有簡單記載使用者跟使用者的一些設定,很多地方有資料不一致的問題,產品內容還有很多實體的關係沒有被定義進資料庫都是這次改版我要做的事情。 改版納入公司、機器
Thumbnail
題目敘述 題目會給我們一組定義好的界面和需求,要求我們設計一個資料結構,可以滿足平均O(1)的插入元素、刪除元素、隨機取得元素的操作。 RandomizedSet() 類別建構子 bool insert(int val) 插入元素的function界面 bool remove(int val
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更