get('filter'); $selectedAgencyId = request()->get('agency_id'); // ตัวแปรสำหรับส่งไปยัง view $agencies = collect(); $currentAgency = null; // ตรวจสอบ level ของผู้ใช้ if (strtolower($user->Level) === 'consult') { // ดึงรายการบริษัทผ่าน AgencyCustomer model $agencyCustomerIds = AgencyCustomer::where('Customer_ID', $user->Customer_ID)->pluck('Agency_ID'); $agencies = Agency::whereIn('Agency_ID', $agencyCustomerIds) ->where('active', 1) ->orderBy('Agency_Name') ->get(); if ($selectedAgencyId) { // ตรวจสอบว่า consult user มีสิทธิ์เข้าถึงบริษัทนี้หรือไม่ $currentAgency = $agencies->where('Agency_ID', (int)$selectedAgencyId)->first(); if (!$currentAgency) { // ถ้าไม่มีสิทธิ์เข้าถึง ให้ redirect กลับ return redirect()->route('products.index')->with('error', 'คุณไม่มีสิทธิ์เข้าถึงข้อมูลของบริษัทนี้'); } } else { // ถ้าไม่ได้เลือกบริษัท ให้เลือกบริษัทแรกเป็นค่าเริ่มต้น $currentAgency = $agencies->first(); $selectedAgencyId = $currentAgency ? $currentAgency->Agency_ID : null; } // ดึงผลิตภัณฑ์ของบริษัทที่เลือก if ($selectedAgencyId && $filter !== 'my') { $products = Product::whereHas('user', function($query) use ($selectedAgencyId) { $query->where('Agency_ID', $selectedAgencyId); })->with('user')->orderBy('DtmIns', 'asc')->get(); } else { // แสดงเฉพาะผลิตภัณฑ์ของตัวเอง $products = Product::where('user_id', $user->Customer_ID)->orderBy('DtmIns', 'asc')->get(); } } else { // สำหรับ user ทั่วไป - ใช้ logic เดิม if ($user->Agency_ID && $filter !== 'my') { $products = Product::whereHas('user', function($query) use ($user) { $query->where('Agency_ID', $user->Agency_ID); })->with('user')->orderBy('DtmIns', 'asc')->get(); // ดึงข้อมูลบริษัทปัจจุบัน $currentAgency = $user->agency; } else { // แสดงเฉพาะผลิตภัณฑ์ของตัวเอง $products = Product::where('user_id', $user->Customer_ID)->orderBy('DtmIns', 'asc')->get(); } } return view('products.index', compact('products', 'agencies', 'currentAgency', 'selectedAgencyId')); } /** * Show the form for creating a new product. */ public function create() { // ตรวจสอบว่ามีข้อมูลผลิตภัณฑ์ที่รอการประเมินหรือไม่ $validated = session('pending_product'); if ($validated) { // ถ้ามีข้อมูลรอการประเมิน ให้แสดงแบบฟอร์มประเมิน $questionGroups = QuestionGroup::active()->with(['questions'])->get(); return view('products.create', compact('validated', 'questionGroups')); } // ถ้าไม่มีข้อมูลรอการประเมิน ให้แสดงฟอร์มเพิ่มผลิตภัณฑ์ return view('products.create'); } /** * Start new product creation (clear session) */ public function createNew(Request $request) { // ลบข้อมูลทั้งหมดจาก session เพื่อเริ่มต้นใหม่ $request->session()->forget('pending_product'); $request->session()->forget('pending_assessment'); return view('products.create'); } /** * Add product and evaluation */ // public function completeProductCreation(Request $request){ // $validated = $request->validate([ // 'name' => 'required|string|max:255', // 'model' => 'nullable|string|max:255', // 'log_receive_method' => 'required|in:syslog_udp,syslog_tcp,syslog_both,other', // 'product_type' => 'required|in:hw_sw_log_server,sw_log_server', // 'description' => 'nullable|string', // 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', // ]); // } /** * Store a newly created product in storage. */ public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'model' => 'nullable|string|max:255', 'log_receive_method' => 'required|in:syslog_udp,syslog_tcp,syslog_both,other', 'product_type' => 'required|in:hw_sw_log_server,sw_log_server', 'description' => 'nullable|string', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', ], [ 'name.required' => 'กรุณากรอกชื่อผลิตภัณฑ์', 'name.max' => 'ชื่อผลิตภัณฑ์ต้องไม่เกิน 255 ตัวอักษร', 'model.max' => 'รุ่นผลิตภัณฑ์ต้องไม่เกิน 255 ตัวอักษร', 'log_receive_method.required' => 'กรุณาเลือกวิธีรับข้อมูล', 'log_receive_method.in' => 'วิธีรับข้อมูลไม่ถูกต้อง', 'product_type.required' => 'กรุณาเลือกประเภทผลิตภัณฑ์', 'product_type.in' => 'ประเภทผลิตภัณฑ์ไม่ถูกต้อง', 'image.image' => 'ไฟล์ที่อัพโหลดต้องเป็นรูปภาพ', 'image.mimes' => 'รองรับเฉพาะไฟล์ JPEG, PNG, JPG, GIF, SVG', 'image.max' => 'ขนาดไฟล์ต้องไม่เกิน 2MB', ]); try { $validated['user_id'] = Auth::user()->Customer_ID; //นำค่า Customer_ID ของผู้ใช้ที่ล็อกอินอยู่ (current user) ไปเพิ่มเข้าไปใน array ที่ชื่อว่า $validated โดยกำหนดให้ key 'user_id' มีค่าเท่ากับ Auth::user()->Customer_ID // จัดการอัพโหลดรูปภาพ if ($request->hasFile('image')) { //ตรวจสอบว่าใน request มีไฟล์ที่ชื่อว่า image แนบมาด้วยหรือไม่ $path = $request->file('image')->store('products_images', 'public');//เก็บไฟล์ที่แนบมานี้ไว้ใน storage ที่ชื่อว่า public/products_images //Laravel จะบันทึกไฟล์และคืนค่า path ของไฟล์นั้น เช่น products_images/abc123.jpg //'public' หมายถึงจะเก็บไว้ใน storage/app/public และสามารถเข้าถึงผ่าน public/storage/ ได้ (ถ้ารัน php artisan storage:link ไว้แล้ว) $validated['image'] = $path;//นำ path ของไฟล์รูปภาพที่อัปโหลด ไปใส่ใน $validated['image'] เพื่อจะได้บันทึกลงฐานข้อมูลพร้อมกับข้อมูลอื่น ๆ } // เก็บข้อมูลผลิตภัณฑ์ไว้ใน session แทนการบันทึกลงฐานข้อมูลทันที $request->session()->put('pending_product', $validated);//เก็บข้อมูลผลิตภัณฑ์ที่รอการประเมินไว้ใน session เพื่อให้สามารถเข้าถึงได้จากหน้า create อีกครั้ง // ดึงข้อมูลคำถามสำหรับการประเมิน (เฉพาะกลุ่มที่ active) $questionGroups = QuestionGroup::active()->with(['questions'])->get();//เป็นการ ดึงข้อมูลจากตาราง question_groups พร้อมกับ โหลดคำถาม (questions) ทั้งหมดมาด้วยแบบ eager loading // ส่งข้อมูลที่เก็บใน session ไปยัง view return view('products.create', compact('validated', 'questionGroups')); } catch (\Exception $e) { // ลบรูปภาพที่อัพโหลดแล้วถ้าเกิดข้อผิดพลาด if (isset($validated['image']) && Storage::disk('public')->exists($validated['image'])) { //ถ้ามีการอัพโหลดรูปภาพไว้ใน $validated['image'] และไฟล์นั้นยังอยู่ใน disk public Storage::disk('public')->delete($validated['image']);//ให้ ลบไฟล์รูปภาพออก (เพื่อไม่ให้ไฟล์ค้างอยู่โดยไม่จำเป็น เพราะบันทึกไม่สำเร็จ) } return back()->withInput()//return back() ส่งกลับไปยัง URL เดิมที่ผู้ใช้กด submit form มา ส่วน withInput() คือ คืนค่า input ที่ผู้ใช้กรอกไว้ (เช่น ชื่อผลิตภัณฑ์ ฯลฯ) ให้กลับมาแสดงใหม่ในฟอร์มเพื่อให้ผู้ใช้ ไม่ต้องกรอกข้อมูลใหม่ทั้งหมด หากเกิดข้อผิดพลาด ->with('error', 'เกิดข้อผิดพลาดในการเพิ่มผลิตภัณฑ์ กรุณาลองใหม่อีกครั้ง');//แนบข้อความ error ไปยัง session flash เพื่อแสดงในหน้า view ตัวอย่าง: ใน blade ใช้ @if (session('error')) เพื่อแสดงข้อความนี้ } } /** * Get products for a specific company. */ public function companyProducts($agencyId)//แสดง ผลิตภัณฑ์ทั้งหมด ที่สร้างโดยผู้ใช้ (user) ซึ่งอยู่ในบริษัทที่มี Agency_ID ตรงกับ $agencyId ที่รับมาจาก URL หรือ route parameter { $products = Product::whereHas('user', function($query) use ($agencyId) { // function($query) {} คือ callback function และ use ($agencyId) คือ ดึงค่า $agencyId ไปใช้ภายใน callback function $query->where('Agency_ID', $agencyId);//ค้นหา products โดยตรวจสอบว่า product นั้น มี user ที่ Agency_ID ตรงกับ $agencyId และ ใช้ whereHas() เพื่อกรอง product โดยอิงจากเงื่อนไขใน customer table (ความสัมพันธ์กับ product) })->with('user')->get();//ดึงข้อมูลผลิตภัณฑ์ทั้งหมด พร้อมกับ โหลดข้อมูลผู้ใช้งานที่สร้างผลิตภัณฑ์นั้น โดยใช้ with() เพื่อลดจำนวน query ที่เกิดขึ้น return view('products.index', compact('products')); } /** * Get products created by the authenticated user only. */ public function myProducts() { $products = Product::where('user_id', Auth::user()->Customer_ID)->get();//ค้นหา products โดยตรวจสอบว่า product นั้น มี user_id ตรงกับ Customer_ID ของผู้ใช้งานที่ล็อกอิน return view('products.index', compact('products')); } /** * Back to edit product information from session */ public function backToEdit(Request $request) { // ลบข้อมูลการประเมินออกจาก session (ถ้ามี) $request->session()->forget('pending_assessment'); // เก็บข้อมูลผลิตภัณฑ์ไว้ใน session เพื่อให้สามารถแก้ไขได้ $validated = session('pending_product'); if (!$validated) { return redirect()->route('products.create') ->with('error', 'ไม่พบข้อมูลผลิตภัณฑ์ที่ต้องการแก้ไข'); } return view('products.create', compact('validated')); } public function destroy(Product $product) { $user = Auth::user(); $hasDeleteAccess = false; // เจ้าของผลิตภัณฑ์ if($user->Customer_ID === $product->user_id) { $hasDeleteAccess = true; } // Admin elseif($user->Level === 'admin') { $hasDeleteAccess = true; } // คนในบริษัทเดียวกัน elseif($user->Agency_ID && $product->user && $product->user->Agency_ID && $user->Agency_ID === $product->user->Agency_ID) { $hasDeleteAccess = true; } if(!$hasDeleteAccess) { return redirect()->route('products.index')->with('error', 'คุณไม่มีสิทธิในการลบผลิตภัณฑ์นี้'); } try { $product->delete(); return redirect()->route('products.index')->with('error', 'ผลิตภัณฑ์ถูกลบอย่างสำเร็จ'); } catch (\Exception $e) { return redirect()->route('products.index')->with('error', 'เกิดข้อผิดพลาดในการลบผลิตภัณฑ์ กรุณาลองใหม่อีกครั้ง'); } } public function report(Product $product){ //1.ตรวจสอบสิทธิ์ - อนุญาตให้เจ้าของผลิตภัณฑ์, คนในบริษัทเดียวกัน, หรือ admin เข้าดูได้ $user = Auth::user(); $hasAccess = false; // เจ้าของผลิตภัณฑ์ if($user->Customer_ID === $product->user_id) { $hasAccess = true; } // Admin elseif($user->Level === 'admin') { $hasAccess = true; } // คนในบริษัทเดียวกัน elseif($user->Agency_ID && $product->user && $product->user->Agency_ID && $user->Agency_ID === $product->user->Agency_ID) { $hasAccess = true; } if(!$hasAccess) { abort(403, 'ไม่มีสิทธิ์เข้าถึงข้อมูลนี้'); } //2.ดึงข้อมูลการประเมิณของผลิตภัณฑ์ พร้อมคำตอบและคำถาม $assessment = $product->assessments() ->with(['answers.question']) ->latest() ->first(); if(!$assessment){ return view('products.report',[ 'product'=>$product, 'assessment' => null, ]); } //3.เรียงลำดับคำตอบตามเลขข้อที่ถูกต้อง และดึงเฉพาะคำถามที่มีคำตอบ $answeredQuestions = collect(); foreach ($assessment->answers as $answer) { $question = $answer->question; if ($question) { // เพิ่มข้อมูลคำตอบเข้าไปในคำถาม $question->answer = $answer; $answeredQuestions->push($question); } } // เรียงลำดับตามเลขข้อที่ถูกต้อง $answeredQuestions = $answeredQuestions->sortBy(function($question) { // ใช้ question_number_sort_key ถ้ามี หรือ fallback ไปใช้ sort_order return $question->question_number_sort_key ?? $question->sort_order ?? 0; }); return view('products.report', compact('product','assessment', 'answeredQuestions')); } } //->with('user') คือ laravel จะยิง 2 query คือ 1.ดึง products 2.ดึง user ที่เกี่ยวข้องทั้งหมด แต่ถ้าไม่ใช้ with ระบบจะยิง 1 query หลัก + N query ย่อย (N = จำนวน products) เรียกว่า N+1 problem //Auth:id คือ ดึง id ของผู้ใช้งานที่ล็อกอิน //Auth:user คือ ดึงข้อมูลผู้ใช้งานที่ล็อกอิน (คืนค่า user model ของผู้ใช้งานที่ล็อกอิน) //create(...) คือ เมธอดของ Eloquent ที่ใช้สร้างเรคคอร์ดใหม่ โดยรับ array ของ key/value ที่ตรงกับชื่อคอลัมภ์ //$validated เป็น array ข้อมูลที่ผ่านการตรวจสอบแล้ว //Eager Loading คือกระบวนการที่ Laravel ทำการ โหลดความสัมพันธ์ (relationships) ของ Model ล่วงหน้าในคำสั่งเดียวกับที่โหลดข้อมูลหลัก เพื่อลดจำนวน query ที่เกิดขึ้นระหว่างการใช้งานฐานข้อมูล (ช่วยแก้ปัญหา N+1 Query Problem) //catch (\Exception $e) { คือ จับข้อผิดพลาดทั่วไป (generic exception) ที่เกิดขึ้นใน try block ก่อนหน้านี้ //isset() คือฟังก์ชันของ PHP ที่ใช้ตรวจสอบว่า ตัวแปรถูกกำหนด (defined) และ ไม่ใช่ค่า null หรือไม่ //exists() ใน Laravel หรือ PHP โดยทั่วไป หมายถึงการ ตรวจสอบว่า "มีอยู่หรือไม่" (เช่น มี record ในฐานข้อมูล หรือมี key ใน array) //back() คือ ฟังก์ชันของ Laravel ที่ใช้ส่งกลับไปยังหน้าที่ผ่านมา (previous page) โดยส่งค่า error message ด้วย //whereHas() ใน Laravel Eloquent ใช้เพื่อ กรอง (filter) ข้อมูลจาก model แม่ โดยอิงตามเงื่อนไขของ ความสัมพันธ์ (relationship) กับ model ลูก //function () use () {} ใน PHP และ Laravel รูปแบบ function () use (...) {} คือการ ประกาศ Anonymous Function (Closure) และใช้ use เพื่อ ดึงตัวแปรจากภายนอกฟังก์ชันเข้ามาใช้ภายใน /* Model::where('column', 'operator', 'value')->get(); // หาก operator เป็น '=' สามารถละได้: Model::where('column', 'value')->get(); */ /* return view('view_name', ['key' => $value]); */ //Session คือ "ที่เก็บข้อมูลชั่วคราวฝั่งเซิร์ฟเวอร์" สำหรับ ผู้ใช้แต่ละคน ที่เข้ามาใช้เว็บแอปพลิเคชัน โดย Laravel จะเก็บข้อมูลนี้ไว้ใน Storage หลายแบบได้ //เก็บข้อมูลไว้ใน session ด้วย session()->put('key', val) //ดึงข้อมูลจาก session ด้วย session()->get('key') หรือ session('key') //ลบข้อมูลจาก session ด้วย session()->forget('key') หรือ session()->pull('key') //ลบข้อมูลทั้งหมดจาก session ด้วย session()->flush() //ตรวจสอบว่า session มีข้อมูลหรือไม่ด้วย session()->has('key')