雖然PHP連線資料庫是一個應該很簡單的過程,甚至已經是標準化的作業了,但是對於沒有程式基礎的人來說,似乎一直有一種”如果不能弄懂它,就不知道要如何使用它”的懸念在,就我的觀察來說,這樣的懸念背後其實是對程式學習的一種潛意識排斥,我所觀察到的可以快速學習程式的人,大多都不會執著於要弄懂程式碼的原理,反而是專注在,先用程式來解決問題上,即使是用背和抄別人的程式碼也無所謂,先過了這一關之後,如果真有學習進修的心,再去探究所謂的原理,而這時因為已經累積了很多實務的操作經驗,再去研究原理就會很容易理解並吸收消化.
回到資料庫撈取資料這件事上,老師提供的範例程式如下
$sql="select * from mygames"; $ro=mysqli_query($link,$sql); $row=mysqli_fetch_assoc($ro); do{ echo $row['g_name']."
"; }while($row=mysqli_fetch_assoc($ro));
首先是設定語法的部份;
$sql=”SELECT * FROM mygames”;
$sql是變數名,這意謂著它不是固定的,你要取成$gamesql也是可以的,實務上,我們在一支php程式中可能不只送出一次查詢,也可能不只查詢一張資料表,也可能下不同的語法,所以$sql並不是唯一不變的.
然後在這裏會把語法字串指定給一個變數只是為了方便之後的引用和程式碼整體的美觀,因為在下一句的資料庫連線查詢語法中我們是可以省略變數,直接代語法進去的;
$ro=mysqli_query($link,$sql);
也可以寫成
$ro=mysqli_query($link,”select * from mygames”);
這會得到相同的結果,而且還省了一個$sql的變數;
那為什麼不直接用後者的寫法?感覺比較省事?
因為實際上我們常常會下很長的語法,甚至會在語法中代入變數,如果直接寫在連線的函數裏,會很難辦識,而且容易出錯,比如這是一個我在使用的語法;
$sql=”SELECT p_course.c_date,p_class_name.c_name as ‘course’ FROM `p_course`,p_class_name WHERE c_date >='”.$get_month_start.”‘ and c_date <='”.$get_month_end.”‘ and c_section=p_class_name.c_no GROUP BY c_date,course”;
整個查詢語句很長,同時還代入了變數,直接放到mysqli_query()中的話,九成會出事.
所以在PHP中的變數除了指定特定的值之外,還有一個重要的工作就是幫我們把一長串的文字或運算結果簡化為一個變數來處理.
因此$ro=mysqli_query($link,$sql);比起$ro=mysqli_query($link,”SELECT p_course.c_date,p_class_name.c_name as ‘course’ FROM `p_course`,p_class_name WHERE c_date >='”.$get_month_start.”‘ and c_date <='”.$get_month_end.”‘ and c_section=p_class_name.c_no GROUP BY c_date,course”);肯定是簡潔多了;
接著我們來看
$ro=mysqli_query($link,$sql);
這一句;
這一句的作用在於連線資料庫,並送出查詢語句,既然送出了查詢語句,那表示我們應該會得到一個查詢的結果,這個結果無法直接echo出來,所以我們先指定給$ro,跟前面一樣,不指定給變數可以嗎?不是不可以,而是不指定給變數的話,後續處理會很麻煩;
這裏要額外提到的是,當查詢的語句成功時,會返回查詢的結果,那如果查詢失敗呢?會得到false,這意謂著,我們也可以利用$ro來做查詢成功與否的判斷,判斷的方式就是看返回的是不是false,但是要注意的是,所謂的查詢成功,在這裏指的是查詢的語法被資料庫接受沒出錯就叫查詢成功,即使得到的查詢結果是沒有任何資料,也算查詢成功,所以我們除了判斷false,有時也得判斷返回的結果是否有資料存在.
再來是 $
row=mysqli_fetch_assoc($ro);
這一句;
mysqli_fetch_assoc($ro);
作用為從$ro中一筆一筆取得資料,然後指定給變數$row,
需要理解的是,我們由資料查詢得到的結果,可能是有多個欄位的多筆資料,這樣的型式程式無法直接處理,因此採用逐筆讀出的方式,
所謂的逐筆讀出是指$row的資料型式會是一個一維陣列,以欄位來間隔資料,像是(大明,90,50,10,32,44)這樣的一筆資料是大明的各科成績,
具體的陣列形態會以鍵值來對應資料,所以完整的$row的內容會是(姓名=>大明,國文=>90,數學=>50,地理=>10,英文=>32,社會=>44),
即然是以鍵值來對應,所以我們在取出資料時,只要指定鍵值就可以了,像是echo $row[‘國文’]會得到90
在開始後面的do..while迴圈時,同學們可以自己做個小實驗,把迴圈先註解後,以逐行的方式執行一次$row=mysqli_fetch_assoc($ro);然後echo $row[‘g_name’];如下
$row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name']; $row=mysqli_fetch_assoc($ro); echo $row['g_name'];
這樣執行16次後,會得到和do..while()迴圈一樣的結果,這就是mysqli_fetch_assoc($ro);逐筆取資料的展現,只是這樣寫很不好看,而且我們無法預知到底要寫多少筆才是資料的結尾,因此採用迴圈來處理重覆執行的作業,同時也利用迴圈來處理判斷是否資料結束,因為當取到沒有下一筆時,會返回false給$row,此時就會結束迴圈了.
最後額外一提,mysqli_fetch_assoc($ro);一定要指定給$row嗎?當然不是一定,但如果不指定會有點麻煩,同學可以直接測試下面的程式碼:
echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>"; echo mysqli_fetch_assoc($ro)['g_name']."<br>";
是否發現一樣可以正確逐筆取得資料?而且還少了一個$row變數?但這樣的用法如果要使用迴圈來重覆取值時,除了格式變得複雜難維護之外,還會遇到不知如何判斷迴圈結束的問題,因為如果直接把.mysqli_fetch_assoc($ro)放到while(mysqli_fetch_assoc($ro))裏的話,那麼這時資料又跳了一筆,然後迴圈裏再去echo mysqli_fetch_assoc($ro)[‘g_name’]時,會出現的是下下一筆資料而不是下一筆資料,因此最終還是得再設一個變數來處理,多此一舉;
這類的例子也很多,日後處理時間時會常遇到,比如我們要取現在當下的時間來做判斷或計算時,我們會使用time()這個函數,然後先指定給一個變數,可能是$get_now=time();,之後都以$get_now來處理時間,為什麼不每次都用time()函數來處理?因為直接引用time()的話,那麼每一段程式得到的時間數值可能都會不一樣,根本無法正確處理.
暫時先解說到這裏,至於要使用while,do…while還是for loop,其實沒有一定,端看使用的目的來決定,同學們現階段該努力的是每天練習打程式碼,至少字不會拚錯,格式不會錯比較重要,如果拚字和格式都還搞不定,一直在執著要弄懂這些原理的話,就好象整天坐在房裏研究各種健身運動對減肥的效用,執著於想要找一種最有效的健身方式來減肥,但光那些研究的時間,都可以出門直接運動好幾輪了,是否應該先出門運動一趟,再回來根據自己的體能狀況調整想要健身的項目會比較妥當一些?
留言
看到這篇真的心有戚戚焉,想起剛學習 PHP 時完全無法理解用迴圈印出 mysqli_fetch_assoc 取出的”東西”時,為何迴圈內寫是同一行程式卻有不同的結果…現在只覺得當初怎麼連這個都不會,不過倒也覺得新手的疑惑挺珍貴的,現在的我能夠清楚的看到程式的架構,看懂每一行程式碼的意思,卻也失去了站在新手角度思考的能力,有時在解答別人的疑問時心裡會覺得這個觀念不就如此,對方為何無法理解? 但其實是自己不夠清楚而嚴謹的表達,即使不得不使用到新的知識點來解釋現有的疑問,也可以透過比喻或黑箱的方式來解釋,至於要展開多少新知識就看對方能不能用自己的話解釋一遍,如果能夠解釋出相同的說法就算是足夠了。 另外我也認同 Mack 大所說,新手先專注於解決問題不必太深究原理,因為新手需要先保持撰寫程式的興趣,而興趣來自於解決問題的正向回饋,但熟悉基礎語法後一定要建立正確觀念,由於 PHP 語言的特性導致用 PHP 當作入門語言的新手們甚至許多 PHPer 對基礎觀念相當模糊,這些人能夠非常有效率的寫 CRUD 卻對 pass by reference、pass by value、shallow copy 這些 pointer 與 reference 等記憶體相關的觀念無所知,也許能寫出購物網站卻無法解釋 cookie 與 session 的原理,也許能夠用 Laravel 框架寫 API,能在 service 與 repository 裡填充業務邏輯卻對物件的分析與設計沒有經驗;當初我也是從職訓 PHP 班開始學習程式,工作一段時間後發覺語言和框架只是熟悉與否而已,真正解決問題的永遠是 system 與 algorithm,熟悉掌握某個程式語言或現代化工具能讓一個人成為熟練工,但紮實的基礎才是決定工程師是否能專業並有創意的解決問題的關鍵。 看到現在最新一期課程的招生訊息真的覺得課程的設計進步很多,雖然還是要考乙級…但增加許多現代化的框架與工具的教學,例如 Laravel、Vue、Git 等等,這些都是我目前工作中有在使用的,相信這些課程能幫助學員在畢業後的求職時更順利;希望那些雖然不是從相關科系畢業卻對寫程式有興趣的人透過這個課程能順利進入這個行業,也祝 Mack 大的教學順利。