خطایابی و Debug کردن برنامه‌ها در php

همانطور که میدونید هنگام توسعه یک برنامه با انواع خطاها روبرو هستیم، برای دیباگ کردن برنامه لازم است در فاز توسعه این خطاها را مشاهده کنیم تا نسبت به دیباگ کردن آنها اقدام کنیم.

در صورتیکه از فریمورک‌هایی همچون Laravel استفاده کنیم نیاز به فعالسازی نمایش خطاها نیست و کافی است در فایل تنظیمات فریمورک آن را اکتیو کنیم ولی گاهی وقتها در برنامه‌های که نسبت به دیباگ آنها اقدام میکنیم از فریمورک خاصی استفاده نشده است پس لازم است بدانیم چطور خطاها را نمایش و دیباگ کنیم. 

۱ –  تنظیم نمایش خطاها 

ini_set ( string $varname , string $newvalue )

ini_set('display_errors', '1');
ini_set('display_errors', 'on');

Sets the value of the given configuration option. The configuration option will keep this new value during the script’s execution, and will be restored at the script’s ending.

۲ – تعیین نمایش سطح خطاها 

int error_reporting ([ int $level ] )

همانطور که میدانید با انواع سطح خطاها روبرو هستیم با استفاده از این تابع تعیین میکنیم که چه نوع خطاهایی نمایش داده شود.

The error_reporting() function sets the error_reporting directive at runtime. PHP has many levels of errors, using this function sets that level for the duration (runtime) of your script. If the optional level is not set, error_reporting() will just return the current error reporting level.

// Report all PHP errors (see changelog)
error_reporting(E_ALL);


// Report all PHP errors
error_reporting(-1);


// Turn off all error reporting
error_reporting(0);

// Same as error_reporting(E_ALL);
ini_set('error_reporting', E_ALL);

سطح خطاهایی که وجود دارند : 

  • E_ERROR : A fatal error that causes script termination
  • E_WARNING : Run-time warning that does not cause script termination
  • E_PARSE : Compile time parse error.
  • E_NOTICE : Run time notice caused due to error in code
  • E_CORE_ERROR : Fatal errors that occur during PHP’s initial startup (installation)
  • E_CORE_WARNING : Warnings that occur during PHP’s initial startup
  • E_COMPILE_ERROR : Fatal compile-time errors indication problem with script.
  • E_USER_ERROR : User-generated error message.
  • E_USER_WARNING : User-generated warning message.
  • E_USER_NOTICE : User-generated notice message.
  • E_STRICT : Run-time notices.
  • E_RECOVERABLE_ERROR : Catchable fatal error indicating a dangerous error
  • E_DEPRECATED : Run-time notices.
  • E_ALL : All errors and warnings (E_STRICT became a part of E_ALL in PHP 5.4)

درصورتیکه فایل php.ini را باز کنید و error_reporting رو جستجو کنید میتوانید تنظیمات آن که کامنت شده است را مشاهده کنید :

error reporting in php.ini file

۱ – Syntax Error ( Parse Error )

این نوع خطاها هنگامی رخ میدهد که در نوشتن سینتکس و ساختار کدهای php مشکلی وجود داشته باشد برای مثال قرارندادن سمیکالن، جاگذاشتن یکی از علامتهای کوتیشن و …

نکته : با رخ دادن parse error کل روند اجرای اسکریپت متوقف می‌شود.

PARSE ERROR

۲ –  Fatal Error

اصطلاحا به آنها خطای مهلک یا کشنده گفته می‌شود. برای مثال وقتی برنامه نویس دو تابع با نام یکسان تعریف می‌کند و یا تابعی که تعریف نشده است صدا زده شود و یا مسیردهی یک فایل php که require شده است اشتباه باشد.

نکته : با رخ دادن fatal error کل روند اجرای اسکریپت متوقف می‌شود.

Fatal Error in php

۳ – Warning Error

این خطاها اجرای برنامه را متوقف نمیکنند. برای مثال وقتی تابعی را فراخوانی می‌کنیم و تعداد پارامترها را اشتباه پاس میدهیم، و یا مسیردهی فایلها وقتی فایلی را include میکنیم را اشتباه بنویسیم و … لازم است تاکید کنم نمایش warning error ها بستگی به کانفیگ سرور نیز دارد.

warning error in php

۴ – Notice Error

این نوع خطاها، خطاهای آگاهی دهنده هستند و روند اجرای برنامه را متوقف نمی‌کنند. برای مثال وقتی از متغیری استفاده کنیم که تعریف نشده باشد و یا در آرایه ای ایندکسی را فراخوانی کنیم که در آرایه وجود نداشته باشد.

Notice Error in PHP

 

در نهایت برای دیباگ کردن برنامه خود لازم است برای تسریع روند کار از دیباگرهایی همچون XDEBUG و Zend استفاده کنیم و روی نرم افزارهایی که کدنویسی میکنیم نصب کنیم.

مطالعه بیشتر :

آماده سازی مک برای یک برنامه نویس

از وقتی روی سیستم عامل مک کار میکنم به جرات میتونم بگم که از کارم صدها برابر بیشتر لذت میبرم. طی این مدت چندین بار پیش اومد که به خاطر مسائلی از اول سیستم عامل مک رو نصب  و راه اندازیش کردم که بعد از راه اندازی آماده سازی برنامه ها برای شروع کار یک برنامه نویس گاهی وقتها زمانبر است. در این مقاله می‌خوام همه برنامه های کاربردی که لازم دارم را معرفی کنم تا بتونم آرشیوی داشته باشم که از چه برنامه هایی برای کارم استفاده میکنم.

Homebrw

معمولا یک برنامه نویس از محیط ترمینال لذت میبره و اگر با لینوکس کار کرده باشید با استفاده از سیستم پکیج کنترلی که داره میتونید همه برنامه های مورد نیازتون رو نصب کنید و … برای همچین امکانی از homebrewاستفاده میکنیم.

قبلا مقاله در رابطه با معرفی پکیج منیجر home brew نوشتم که میتوانید آن را مطالعه کنید.

نصب آخرین ورژن php

همانطور که در توضیحات بالاتر خواندید به راحتی با استفاده از پکیج منیجر homebrew برای نصب پکیج‌‌ها و ابزارهای موردنظر اقدام می‌کنیم.

نکته :

برای نصب php با استفاده از homebrew h اگر قبلا ورژنی از phpنصب کرده اید و میخواهید آن را آپگرید کنید ابتدا همه پکیج‌ها و خود php را پاک کنید و سپس برای نصب آخرین ورژن آن اقدام کنید.

این مقاله ” How to upgrade from PHP 7.1 to 7.2 on MacOS using Homebrew ” برای آپگرید ورژن php  روی homebrew کمک میکند.

همچنین مطالعه این مقاله ” Install PHP 7.2 + xdebug on MacOS High Sierra  ” و ” macOS 10.13 High Sierra Apache Setup: Multiple PHP Versions ” برای نصب php  بهتون کمک میکند.

نکته : اگر تاکنون php را روی homebrew نصب نکردید با اجرای دستور brew install php  به راحتی آخرین ورژن آن را نصب می‌کنید.

با توجه به اینکه High Sierra به صورت پیشفرض نسخه ۷.۱ php را داشت بعد از نصب php  با استفاده از پکیج منیجر homebrew  بازهم ورژن پیشفرض را فراخوانی میکرد. برای حل این مشکل داخل فایل .bash_profile  مسیر نسخه جدید php را قرار دادم. ( export PATH=”/usr/local/Cellar/php/bin:$PATH”)

install new version of PHP

نصب آپاچی با استفاده از homebrew

در آخرین ورژن سیستم عامل مکینتاش ( macOS 10.13 High Sierra ) آپاچی ورژن ۲.۴ به صورت پیشفرض نصب شده است ولی با توجه به اینکه اپل برخی از اسکریپت‌های ضروری را حذف کرده است. بهترین کار این است که با استفاده از homebrew آپاچی را دوباره نصب کنیم و آپاچی پیشفرض را غیرفعال کنیم.

ابتدا آپاچی پیشفرض را خاموش و در نهایت لود شدن اتوماتیک اسکریپت‌های آن را غیرفعال می‌کنیم. با استفاده از دو دستور زیر اینکار را انجام می‌دهیم.

sudo apachectl stop
sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist 2>/dev/null

حالا با استفاده از دستور زیر نصب وبسرور را شروع میکنیم.

brew install httpd

بعد از نصب پیامی مشابه تصویر زیر در پایان نمایش داده می‌شود.

brew install httpd

برای چک کردن نصب آن با اجرای دستور which apachectl باید مسیر /usr/local/bin/apachectl چاپ شود.

اگر مراحل نصب به درستی انجام شده باشد با اجرای دستور apachectl start و باز کردن آدرس http://localhost:8080 باید پیام زیر نمایش داده شود.

it works!

که با توجه به توضیحات پایان نصب آپاچی داکیومنت روت آن به صورت زیر است که فایل index.html با پیام موفقیت آمیز فوق آنجا قرار داده شده است.

document root apachectl

کانفیگ آپاچی ( Apache Configuration )

اکنون که وبسرور آپاچی را نصب کردیم برای استفاده راحتتر و بهتر یکسری کانفیگ‌هایی را برای آن تنظیم می‌کنیم.

در آخرین ورژن Brew که آپاچی را نصب کردیم به صورت پیشفرض روی پورت ۸۰۸۰ فعال است که ما آن را به ۸۰ تغییر می‌دهیم. برای اینکار فایل httpd.conf در مسیر زیر را ویرایش میکنیم.

/usr/local/etc/httpd/httpd.conf

برای باز کردن فایل کافی است دستور زیر را در ترمینال وارد کنید و یا به مسیر مورد نظر بروید و دستی فایل را با TextEditorباز کنید.

open -e /usr/local/etc/httpd/httpd.conf

خطی که به صورت زیر نوشته شده است را پیدا میکنیم و به Listen ۸۰ تغییر می‌دهیم.

Listen 8080

حالا داکیونت روت آن را ویرایش میکنیم، به صورت پیشفرض Document Root آن روی مسیر /usr/local/var/www فعال است که به مسیر زیر تغییر می‌دهیم.

DocumentRoot /Users/your_user/Sites

نکته : به جای your_user نام کاربری خود را بنویسید.

بعد از آن نیاز است که <Directory> را هم که زیر آن نوشته شده است را ویرایش کنید به صورت زیر :

<Directory /Users/your_user/Sites>

داخل بلاک Directory شما باید AllowOverride را پیدا کنید و آن را به صورت زیر تغییر دهید.

# AllowOverride controls what directives may be placed in .htaccess files.
# It can be "All", "None", or any combination of the keywords:
#   AllowOverride FileInfo AuthConfig Limit
#
AllowOverride All

اکنون باید mod_rewrite را که به صورت پیشفرض غیرفعال است فعال کنیم. mod_rewrite.so را جستجو کنید و از حالت کامنت خارج کنید تا فعال شود. ( علامت # اول آن را پاک کنید. )

LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so

تقریبا تنظیمات آپاچی تمام شد ولی الان مشکل دسترسی به مسیر Sites داریم. به صورت پیشفرض آپاچی با یوزر daemon یا _www اجرا می‌شود که باید کدی به صورت زیر پیدا کنید :

User _www
Group _www

که آن را به صورت زیر تغییر می‌دهیم.

User your_user
Group staff

آپاچی میتواند یک سرورنیم داشته باشد ولی به صورت پیشفرض غیرفعال است که کافیه آن را پیدا کنید که به صورت زیر نوشته شده است:

#ServerName www.example.com:8080

و از حالت کامنت خارج کنید و به صورت زیر بنویسید.

ServerName localhost

حالا که تنظیمات تمام شد فولدر Sites  را در  home directory بسازید و فایل index.html داخل آن قرار دهید تا تست آخر را انجام دهید. با استفاده از دستور زیر در ترمینال اینکار را به سادگی انجام دهید.

mkdir ~/Sites
echo "<h1>My User Web Root</h1>" > ~/Sites/index.html

حالا آپاچی را رستارت ( sudo apachectl restart ) و سپس روی مرورگر آدرس http://localhost را باز کنید.

نکته مهم: در صورتیکه هنگام رستارت آپاچی با خطا مواجه شدید دقت کنید که : اگر یادتان باشد Document Root و Directory  را که ویرایش کردیم مسیر جدید را داخل کوتیشن “”  قرار دادیم خطای رخ داده به خاطر این مورد است که کافیه آنها را حذف کنید و بدون کوتیشن باشند. و دوباره اقدام کنید که بعد از رستارت تصویر زیر در مرورگر نمایش داده می‌شود.

localhost

کانفیگ PHP روی آپاچی! ( Apache PHP Setup )

تا به اینجا ما با موفقیت آپاچی و php را نصب کردیم. اما ما نیاز داریم تا به آپاچی بگیم که از این php  که نصب کردیم استفاده کند. مجددا فایل /usr/local/etc/httpd/httpd.conf را باز میکنیم و ویرایش را به صورت زیر انجام می دهیم.

به صورت زیر libphp را فعال می‌کنیم.

#LoadModule php5_module /usr/local/opt/[email protected]/lib/httpd/modules/libphp5.so
#LoadModule php7_module /usr/local/opt/[email protected]/lib/httpd/modules/libphp7.so
#LoadModule php7_module /usr/local/opt/[email protected]/lib/httpd/modules/libphp7.so
LoadModule php7_module /usr/local/opt/[email protected]/lib/httpd/modules/libphp7.so

در هرلحظه ما فقط میتوانیم یکی از این ورژن‌های php را فعال کنیم.

اکنون ما باید دایرکتوری index را برای php تعریف کنید. پس به دنبال بلاک زیر بگردید.

<IfModule dir_module> 
DirectoryIndex index.html 
</IfModule>

که به صورت زیر ویرایش میکنیم:

<IfModule dir_module>
 DirectoryIndex index.php index.html 
</IfModule> 

<FilesMatch \.php$> 
SetHandler application/x-httpd-php 
</FilesMatch>

تغییرات را ذخیره و آپاچی را ریستارت میکنیم.

 sudo apachectl -k stop
 sudo apachectl start

اعتبارسنجی php

برای اینکه بدانیم php را و آپاچی را به درستی کانفیگ کردیم در home directory فایل info.php را ایجاد می‌کنیم و با نوشتن دستور phpinfo() مشخصات php در آدرس http://localhost/info.php  برای ما نمایش داده می‌شود. میتوانید دستی فایل را ایجاد کنید و یا کد زیر را در ترمینال وارد کنید.

echo "<?php phpinfo();" > ~/Sites/info.php

که صفحه موردنظر به صورت زیر نمایش داده می‌شود.

phpinfo

ایجاد دامنه مجازی ( Apache Virtual Hosts )

قبلا در مقاله ای در رابطه با ایجاد دامنه مجازی روی XAMPP توضیح دادم که به چه شیوه ای اینکار انجام شود. اما با توجه به اینکه در این مقاله ما خودمون mysql, php , apache را نصب کردیم نحوه ایجاد آن کمی متفاوت است.

آپاچی به صورت پیشفرض امکان ایجاد دامنه مجازی را دارد اما غیرفعال است. ابتدا خط زیر را در فایل  /usr/local/etc/httpd/httpd.conf پیدا میکنیم و از کامنت خارج میکنیم.

LoadModule vhost_alias_module lib/httpd/modules/mod_vhost_alias.so

و همچنین این خط را از کامنت خارج کنید.

# Virtual hosts
Include /usr/local/etc/httpd/extra/httpd-vhosts.conf

پس از آن فایل /usr/local/etc/httpd/extra/httpd-vhosts.conf را یا به صورت دستی و یا با استفاده از ترمینال با کد زیر باز میکنیم.

open -e /usr/local/etc/httpd/extra/httpd-vhosts.conf

بعد از اینکه فایل را باز کردید فولدرهای پروژه تان را که در داکیومنت روت Sites ایجاد کردید را مسیردهی میکنید به صورت زیر

<VirtualHost *:80>

 DocumentRoot "/Users/your_user/Sites"
 ServerName localhost 

</VirtualHost>

پروژه خود را به صورت مثال زیر ایجاد کنید.

<VirtualHost *:80>
    DocumentRoot "/Users/your_user/Sites/example"
    ServerName example.test
</VirtualHost>

بعد از آن وارد فایل hosts  را باز میکنیم ( nano /etc/hosts ) و دامنه ای که ایجاد کردیم را در فایل hosts قرار می‌دهیم.

۱۲۷.۰.۰.۱  example.test

بعد از آن آپاچی را ریستارت می‌کنیم.

 sudo apachectl -k stop
 sudo apachectl start

نکته : اگر هنگام ریستارت آپاچی با خطا مواجه شدید، کوتیشن‌هایی که در مسیر DocumentRoot وارد شده است را حذف کنید و مجددا اقدام کنید.

حالا اگر روی مرورگر آدرس example.test را باز کنید مسیر فولدر example در Sites را نمایش میدهد.

ایجاد SSH Key برای گیت لب

در صورتیکه پروژه‌هایی روی گیت‌هاب و یا گیت‌لب داشته باشید قطعا باید ssh key برای اعتبارسنجی اکانت شما وجود داشته باشد.

قبلا در مقاله‌ای هم آرشیوی از دستورات کاربردی گیت را نوشتم و هم نحوه ایجاد و تنظیم ssh key را توضیح دادم.

نصب composer

قطعا برای استفاده از پکیج‌ها و ماژول‌های php به composer نیاز خواهیم داشت، با استفاده از پکیچ منیجر homebrew  به راحتی آن را نصب میکنیم.

brew install composer 

brew install composer

ایجاد alias های موردنیاز

بسیاری از دستورات و کامندها هستند که تکراری هستند و تایپ و تکرار آنها زمانبر و گاهی وقتها خسته کننده ست، مثلا من خودم برای دسترسی به مسیر پروژه هر سری توی ترمینال مسیر ان را بنویسم و کامندها را در آن مسیر بنویسیم برایم خسته کننده ست به همین خاطر برای هر پروژه با اسم پروژه یک alias ایجاد میکنم که با تایپ اسم پروژه به مسیر موردنظر میروم. قبلا برای ایجاد alias مقاله ای نوشتم که میتوانید با کمک آن اینکار را انجام دهید.

نصب mysql , postgres

مطابق توضیحات بالا با استفاده از پکیج منیجر Brew برای نصب اقدام می‌کنیم.

نکته : بعد از نصب postgres ، باید دیتابیس و یوزری ایجاد شود تا با استفاده از نرم افزارهایی همچون postico بتوان لاگین شد و مدیریت دیتابیس را داشت. این صفحه “Installing Postgres via Brew” در این مورد به شما کمک می‌کند.

نکته : برای نصب mysql با استفاده از homebrew به راحتی اینکار را انجام می‌دهیم. و بعد از نصب مطابق توضیحاتی که در پایان نصب نمایش میدهد اقدام میکنیم. شما می‌توانید از این ویدئو ”  Install MySQL via Homebrew ” برای نصب کمک بگیرید.

در نهایت چند alias  برای start, stop و restart آن ایجاد میکنم تا هر موقع نیاز به آنها بود سریع اقدام کنم به صورت زیر :

create alias for mysql

نکته : برای مدیریت mysql میتوانید از برنامه mysql workbench استفاده کنید که با استفاده از دستور زیر میتوانید آن را نصب کنید و یا فایل آن را دانلود و نصب کنید.

brew cask install mysqlworkbench

معرفی پکیج منیجر homebrew

معمولا برنامه نویس از محیط ترمینال لذت میبره و اگر با لینوکس کار کرده باشید با استفاده از سیستم پکیج کنترلی که داره میتونید همه برنامه های مورد نیازتون رو نصب کنید و … قبلا برای راه اندازی وب سرور و mysql و php از برنامه XAMPP، یا MAMP استفاده می‌کردم. با نصب یکی از این برنامه ها همه موراد موردنظر برامون نصب می‌شد و کار کردن در این محیط خیلی راحت و ساده است ولی برای ارتقای مثلا ورژن php و mysql دردسرهای خاص خودشون رو دارند.

با استفاده از Homebrew  دقیقا به سبک پکیج کنترل لینوکس میتونیم عمل کنیم و ابزارهای موردنیازمون رو نصب کنیم. ابتدا Homebrew رو به صورت زیر نصب می‌کنیم:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

آرشیو کامندهای homebrew :

۱. جستجوی ابزار مورد نظر ( مثلا mysql )

brew search mysql

که همه پکیج‌هایی که پیدا می‌شود را نمایش می‌دهد به صورت زیر :

۲. نصب ابزار مورد نظر ( مثلا mysql )

brew install mysql

بعد از نصب آن اطلاعات آن به صورت زیر نمایش داده می‌شود.

brew install mysql

ست کردن رمز عبور root برای mysql :

mysqladmin -u root password 'yourpassword'

دقت کنید که پسورد داخل سنگل کوتیشن باشد.

۳. مشاهده لیست ابزارهای نصب شده

brew list

با استفاده از دستور  زیر لیست پکیج‌هایی که برای php نصب شده اند را مشاهده میکنیم.

brew list | grep php

۴. مشاهده اطلاعات ابزار نصب شده ( مثلا mysql )

brew info mysql

که اطلاعات آن به صورت زیر نمایش داده می‌شود

brew info mysql

۵. بروزرسانی homebrew

ابتدا تمام ابزارهای استفاده شده را آپدیت می‌کند و درنهایت خود پکیج منیجر را بروزرسانی می‌کند.

brew update

brew update

۶. مشاهده config

brew config

۷. مشاهده راهنمای کامندها homebrew

brew help

brew help

۸. مشاهده لیست سرویس‌های فعال

brew services list

در تصویر زیر مشاهده می‌کنید که mysql فعال است.

brew services list

۹. فعال/غیرفعال کردن یک سرویس ( برای مثال mysql )

brew services start/stop mysql

brew services start mysql

۱۰. عیب‌یابی پکیج منیجر homebrew

brew doctor

در صورت ارتقا سیستم عامل و … اینکه اگر احساس کردید پکیج منیجر شما دچار ایرادی شده است با استفاده از کامند فوق میتوانید ایراد موردنظر را پیدا کنید و پکیج منیجر homebrew  ایراد مورد نظر را به شما نمایش می‌دهد در غیر اینصورت پیام ” Your system is ready to brew. ” را نمایش می‌دهد.

۱۱. ارتقا پکیج منیجر و ابزارهای نصب شده

با استفاده از دستور زیر تمام ابزارها و خود پیکج منیجر ارتقا داده می‌شوند.

brew upgrade

در صورتیکه بخواهیم یک ابزار خاص را ارتقا دهیم به صورت زیر اقدام می‌کنیم (به جای formulaنام ابزار موردنظرمان را می‌نویسیم)

brew upgrade "formula"

۱۲. محدود کردن برخی ابزارها برای بروزرسانی و ارتقا

در صورتیکه بخواهیم برخی ابزارها را محدود کنیم و هنگامی که از آپدیت و آپگرید استفاده کردیم بروزرسانی نشوند از دستور زیر استفاده می‌کنیم

To stop something from being updated/upgraded

brew pin formula

برای رفع این حالت محدودیت :

To allow that formulae to update again

brew unpin formula

۱۳. پاک کردن ورژن‌های قدیمی ابزارهای نصب شده

به صورت پیشفرض، homebrew ورژن‌های قدیمی پکیج‌ها را پاک نمیکند به همین خاطر لازم است با استفاده از دستور زیر ورژن قدیمی پکیج موردنظر را پاک کنیم.

brew cleanup formula

و یا اینکه همه ورژن‌های قدیمی همه بخشها را پاک کنید.

brew cleanup

برای مشاهده اینکه کدام یک از پکیج‌ها ورژن‌های قدیمی را دارند از دستور زیر استفاده می‌کنیم.

brew cleanup -n

۱۴. حذف پکیج منیجر Homebrew

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"

۱۵. حذف یک پکیج مشخص

brew uninstall formula_name --force

که بعد از حذف آن با اجرای دستور brew cleanup تمام فایلهای آن نیز حذف میشوند.

۱۶. ورژن پکیج منیجر homebrew

brew --version

۱۷. brew tap

با استفاده از کامند tap اجازه به homebrew داده می‌شود که به ریپازیتوری دیگری اشاره کند. پس از استفاده از این دستور homebrew برای نصب ابزار مورد نظر آماده است.

brew tap      # list tapped repositories
brew tap            # add tap
brew untap          # remove a tap

brew tap <user/repo> makes a shallow clone of the repository at https://github.com/user/repo. After that, brew will be able to work on those formulae as if they were in Homebrew’s canonical repository. You can install and uninstall them with brew [un]install, and the formulae are automatically updated when you run brew update. (See below for details about how brew tap handles the names of repositories.)

بعد از اجرای brew tap را در ترمینال وارد کنیم لیست ریپازیتوری تب‌های فعلی ( currently tapped repositories ) نمایش داده می‌شود.

brew tap

نصب نرم افزارهای کاربردی با Brew

همانطور که اشاره کردم با پکیج منیجر home brew به راحتی ابزارهایی که نیاز داریم را نصب میکنیم و معمولا برنامه ها و اپلیکیشن هایی همچون Chrome، Insomnia و…. نسخه مک آن با فرمت dmg را از سایت موردنظر دانلود و نصب میکنیم. اما brew با استفاده از قابلیت Homebrew-Cask این امکان رو بهمون میده که نرم افزارها رو هم نصب کنیم.

Homebrew-Cask extends Homebrew and brings its elegance, simplicity, and speed to the installation and management of GUI macOS applications such as Atom and Google Chrome.

برای مثال برای نصب Insomnia با دستور زیر اقدام می‌کنیم.

brew cask install insomnia

brew cask install insomnia

دریافت مقدار انتخاب شده select

برای یکی از پروژه هایی که قبلا توسعه می‌دادم نیاز به ساخت یک فیلتر آجاکسی بر روی لیستی از دیتاها بود که وقتی کاربر بر روی select کلیک میکرد مقدار موردنظر با آجاکس ارسال و جستجو انجام میشد در واقع بروزرسانی مقادیر تگ select باید انجام می‌شد.

select2 plugin

$('select.selectronFilter').on('change', function(){
    var selected    = $(this).find("option:selected").val();
});

با استفاده از کد فوق به راحتی می‌تونیم مقدار option مورد نظر از select  رو برگردونیم و با استفاده از API که ایجاد کردیم مقادیر مورد نظر را نمایش دهیم.

اما مساله ای که مطرح است اینه که اگر ما چندین select داشته باشیم و مقدار دومی وابسته به مقدار انتخابی اولی باشد و سومی نیز وابسته به مقدار انتخابی دومی و … باشد. برای مثال وقتی کشور را انتخاب می‌کنیم استان/ایالت های آن کشور و وقتی استان/ایالت را انتخاب کردیم شهرهای آن استان/ایالت نمایش داده می‌شود.

انتخاب مقدار تگ select و بروزرسانی داده با Ajax

در صورتیکه از AJAX استفاده کنیم بعد از انتخاب کشور مقادیر استان/ایالت را برمیگردانیم و داخل select مورد نظر نمایش میدهیم. همه چیز به درستی کار میکنه ولی وقتی از Bootstrap Select یا Select 2 استفاده می‌کنیم برای اینکه دیزاین کاربرپسندی داشته باشیم قضیه به همینجا ختم نمیشه!

قبلا در رابطه با برخی پلاگین‌های کاربردی جاوا اسکریپت مطلبی نوشتم که می‌توانید با برخی از پلاگین‌های کاربردی آشنا شوید و در پروژه های خود از آنها استفاده کنید.

همانطور که میدانیم این دو پلاگینی که معرفی کردم ( ‌Bootsrap Select , Select 2 ) هر دو با استفاده از JavaScript  مقادیری option را داخل یکسری تگ html دیگر قرار می‌دهند تا ظاهری که زیبا است را نمایش دهند چرا که بر روی تگ select نمیتوان استایل css اعمال کرد و محدودیت دارد!

وقتی مقادیر را دریافت میکنیم و آنها را داخل select قرار میدهیم باید آن را رفرش کنیم تا مقادیر جدید جایگزین شوند.

مثال زیر را مشاهده کنید، مقادیر را دریافت و تگ‌های ‌‌option ایجاد و مقادیر داخل آنها قرار داده میشوند و در نهایت داخل select قرار میگیرند، بعد از اینکار باید پلاگین استفاده شده را رفرش کنیم تا داده های جدید قرارگیرند.

$.ajax({
    url           : 'url',
    type         : 'POST',
    data        : formData,
    contentType   : false,
    processData   : false,
    success: function(response) {

        var arrOption = new Array();
        arrOption += "<option value=''>انتخاب موضوع</option>";

        $.each( response, function( key, value ) {
          arrOption += "<option value='" + key + "'>"+ value +"</option>";
        });

        $('select[name=example]').html(arrOption);
        $('select[name=example]').selectpicker('refresh');
    }
});

پلاگین Bootstrap Select :

در صورتیکه از پلاگینBootstrap Select استفاده کنیم به صورت زیر پلاگین را رفرش میکنیم و مقادیر جدید قرار داده می‌شوند. ( متدهای پلاگین )

$('.rm-mustard').click(function () {
  $('.remove-example').find('[value=Mustard]').remove();
  $('.remove-example').selectpicker('refresh');
});

پلاگین select2 :

در صورتیکه از پلاگین select2 استفاده می‌کنید به صورت زیر اقدام می‌کنیم: ( متدهای پلاگین )

$(".js-programmatic-set-val").on("click", function () {
    $example.val("CA").trigger("change");
});

 

آرشیو دوم : دستورات و نکات مهم لاراول

در مقاله قبل آرشیوی از دستورات اولیه و مهم لاراول رو نوشتم تا هر موقع نیاز شد بازبینی داشته باشم. در آرشیو دوم سعی میکنم برخی مفاهیم جدید که یاد میگیرم را به اشتراک بذارم.

۱ – تفاوت بین PUT و PATCH  در فرم‌های لاراول :

وقتی یک فرم با متد post ارسال می‌شود یک resource  در سرور ایجاد می‌شود. اگر کاربر درخواست مشابهی رو دوبار وارد کند برای درخواست دوم resource جدید ایجاد می‌شود. در واقع ارسال فرم با متد post همان INSERT یک رکورد در دیتابیس است.

http post method is like a INSERT query in SQL which always creates a new record in database.

برای مثال ثبت یک مطلب، کاربر و سفارش در وبسایت همگی با متد post انجام می‌شود.

put و patch درخواست های http هستند که هر دوی آنها اطلاعات را بروزرسانی می‌کنند.

متد PUT :

ارسال فرم با متد put  به این صورت عمل می‌کند ابتدا با استفاده از شناسه ای که دریافت می‌کند ابتدا بررسی می‌کند تا از موجودیت رکورد مورد نظر در دیتابیس مطمئن شود اگر رکورد وجود داشت همان رکورد را بروزرسانی میکند و درواقع اطلاعات جدید ارسال شده کاملا جایگزین می‌شود ولی اگر بعد از بررسی دیتایی وجود نداشت در واقع آن یک resource جدید در سرور ایجاد می‌کند و اطلاعات را در دیتابیس ثبت میکند.

http put method is like a MERGE query in SQL which inserts or updates a record depending upon whether the given record exists.

در متد PUT اگر چند درخواست مشابه ارسال شود با توجه به شناسه ای که ارسال می‌شود همان resource که پیدا شده است بروز رسانی می‌شود و ‌resource جدیدی در سرور ایجاد نمی‌شود. در واقع در این متد شناسه ‌resource توسط کاربر ارسال می‌شود.

برای مثال وقتی سفارش مشتری، مطلب وبلاگ و یا کاربری که وجود دارد را بروزرسانی می‌کنیم.

متد PATCH :

از متد patch  برای بروزرسانی بخشی از یک  resource (داده، اطلاعات) استفاده می‌کنیم. در واقع در این متد همه اطلاعات ارسال نمی‌شود که کاملا جایگزین رکورد فعلی دیتابیس شود.

http patch method is like a UPDATE query in SQL which sets or updates selected columns only and not the whole row.

برای مثال وقتی وضعیت یک سفارش مشتری را تغییر میدهیم از این متد استفاده می‌کنیم. ( PATCH request requires less bandwidth. )

۲ – ست کردن Session  بعد از ریدایرکت :

وقتی رکوردی در دیتابیس اضافه می‌شود و یا تغییری اعمال می‌شود، ما یک session به صفحه ارسال می‌کنیم تا تغییر مورد نظر را تایید و یا رد کند. به صورت زیر این کار را انجام می‌دهیم:

 return redirect()->back()->with('success', ['your message,here']);   

برای نمایش پیام ست شده در blade  به صورت زیر عمل می‌کنیم :

@if (\Session::has('success'))
  <div class="alert alert-success">
    <ul>
       <li>{!! \Session::get('success') !!}</li>
    </ul>
  </div>
@endif

۳ – نکته ای در مورد migration :

در پروژه ای که انجام می‌دادم نیاز بود یکی از فیلدهای دیتابیس را از حالت UNIQUE خارج کنم. برای اینکار یک مایگریشن ایجاد کردم و به صورت زیر اجرا کردم.

public function up()
{
    Schema::table('users', function(Blueprint $table)
    {
        $table->dropUnique('email');
    });
}

پس از اجرای مایگریشن، متوجه شدم که به درستی اجرا نمیشه و عمل نمیکنه. نکته ای که وجود دارد این است که :

To drop an index you must specify the index’s name. Laravel assigns a reasonable name to the indexes by default. Simply concatenate the table name, the names of the column in the index, and the index type.

وقتی فیلد را UNIQUE  کردم ایندکس شد و برای اینکه از حالت UNIQUE خارجش کنم باید نام ایندکس آن را داخل مایگریشن قرار دهم به صورت زیر :

$table->dropUnique('users_email_unique');

در قسمت دیگری که مایگریشن را ایجاد می‌کردم، باید فیلد دیتابیسی که کلید خارجی بود را از حالت کلید بودن بر می‌داشتم برای اینکار باید dropForeign با سبکی نوشته میشد که مطابق قاعده مایگریشن ها باشد.

برای مثال وقتی در جدول boxes فیلدی به نام type_id داریم و کلید خارجی به جدول دیگری است

$table->integer('type_id')->unsigned();
$table->foreign('type_id')->references('id')->on('table');

باید برای  dropForeign آن به صورت زیر اقدام میکردم :

<table_name>_<column_name>_foreign

یعنی به صورت زیر :

$table->dropForeign('boxes_type_id_foreign');

۴ – دریافت اولین و آخرین مقدار Foreach در قالب Blade Laravel :

از نسخه لاراول ۵.۳+ متغیری به نام $loop داخل حلقه ها در دسترس است که میتوانیم کارهای زیر را انجام دهیم  :

شمارنده در هر اجرا : در لیست هایی که چاپ میکنیم میتوانیم شمارنده ای را با استفاده از این متغیر چاپ کنیم :

$loop->iteration 

دریافت اولین و آخرین مقدار یک آرایه :

@foreach ($menu as $item)
  <div @if ($loop->first) class="hidden" @endif>
    <h2>{{ $item->title }}</h2>
  </div>
@endforeach
@foreach ($menu as $item)
   <div @if ($loop->last) class="hidden" @endif>
     <h2>{{ $item->title }}</h2>
   </div>
@endforeach

۵ – فیلدهای دیتابیس از نوع JSON و نحوه Eloquent

وقتی داده‌هایی که در دیتابیس ذخیره می‌کنیم از نوع JSON باشند فقط یک مقدار را ندارد که به صورت روال قبل داده را دریافت کنیم و از آن در eloquent استفاده کنیم. مثلا داده های json زیر داخل یکی از فیلدهای دیتابیس به نام meta ذخیره شده است :

{
    "id": 1,
    "name": "Alphonse",
    "meta": {
        "wants_newsletter": true,
        "favorite_color": "red"
    }
}

برای اینکه چک کنیم در صورتیکه یکی از ویژگی‌های فیلد meta  مطابق چیزی است که ما میخواهیم یا خیر به صورت زیر اقدام میکنیم، مثلا میخواهیم در صورتیکه مقدار favoraite_color  اگر red بود داده را بگیریم :

$redLovers = DB::table('users')
    ->where('meta->favorite_color', 'red')
    ->get();

برای بروزرسانی هم به صورت زیر میتوانیم بنویسیم :

DB::table('users')
    ->where('id', 1)
    ->update(['meta->wants_newsletter' => false]);

نکته مهم : MariaDB در حال حاضر از فیلدهای دیتابیس با نوع JSON پشتیبانی نمی‌کند ولی PostgreSQL و MySQL 5.7+ از این نوع فیلد پشتیبانی می‌کنند.

۶ –  والیدیشن فیلد ایمیل ( یونیک بودن ایمیل )

در فرم‌هایی که ایجاد میکنیم، برای مثال وقتی که میخواهیم پروفایل کاربر را ادیت کنیم و در جدول کاربران فیلد ایمیل یا هر فیلد دیگری یونیک است در رکوستی که برای والیدیت ( Validation )  فیلدهای ارسالی مینویسیم به صورت زیر اقدام می‌کنیم :

$rules = [
'email'     => 'nullable|email|unique:users,email,' . $this->user,
]

فیلد ایمیل را nullable کردیم و آن را اختیاری قرار دادیم از طرفی می‌خواهیم این فیلد برای هر کاربر یونیک باشد پس آن را برای جدول users یونیک می کنیم.

وقتی ایمیل کاربر تغییر میکند، یونیک بودن را با سایر فیلدهای دیتابیس چک کند و این رکورد از دیتابیس را با بقیه مقایسه کند. ( در صورتیکه رکورد کاربر فعلی که ادیت می‌شود را مشخص نکنیم ( $this->user ) خطای تکراری بودن ایمیل باعث می‌شود نتوانیم کاربری را ویرایش کنیم. )

ساخت Helper کاستوم در لاراول

وقتی پروژه ای را پیاده سازی می‌کنیم بعد از پیاده سازی ساختار پروژه، به فایلهایی همچون config فکر می‌کنیم که در تمام بخش‌های پروژه فراخوانی شود و فایلی رو داشته باشیم که قبل از نمایش صفحه فانکشن‌هایی برای آن پروژه اجرا شود.

اخیرا در پروژه ای که با لاراول پیاده سازی شده بود، نیاز داشتم که یک فانکشن در تمام بخش‌های پنل کاربری اجرا شود. برای اینکار نیاز به یک فایل به نام Helper داشتم که قبل از اجرای صفحه لود شود و موارد موردنیازم را بررسی و اجرا نماید. پس فایل کاستوم Helper را ایجاد کردم.

مسیرهایی که برای این فایل در نظر گرفته می‌شود معمولا یکی از دو مورد زیر است :

  • app/helpers.php
  • app/Http/helpers.php

لازم است که این فایل را در کل پروژه فراخوانی کنیم.

require_once ROOT . '/helpers.php';

ولی در لاراول می‌توانیم از یک راه جذابتر استفاده کنیم و مسیر فایل Helper  را در فایل composer.json لاراول ( autoload ) تعریف کنیم.

"autoload": {
    "files": [
        "app/helpers.php"
    ],
    "classmap": [
        "database/seeds",
        "database/factories"
    ],
    "psr-4": {
        "App\\": "app/"
    }
},

بعد از اینکار کامند زیر را اجرا میکنیم :

composer dump-autoload

با اینکار فایل helper.php  در تمام کنترلرهایی که داریم در دسترس است و میتوانیم استفاده کنیم به این خاطر که وقتی مسیر فایل را در autoload کامپوزر لاراول قرار دادیم و چون autoload لاراول در مسیر public/index.php به صورت زیر قرار می‌گیرد پس فایل helper نیز در دسترس قرار می‌گیرد :

require __DIR__.'/../vendor/autoload.php';

بعد از ایجاد فایل Helper  لازم است به صورت زیر تابع‌هایی که نیاز داریم را تعریف کنیم :

if (! function_exists('function_name')) 
{
    function function_name($key, $default = null) 
{
        // ...
    }
}

 

ابزارهایی برای تحلیل و دیاگرام دیتابیس

با توجه به اینکه تقریبا بیش از نیم سالی است تمرکز زیادی روی بخش بک‌اند دارم و روی تحلیل دیتابیس و ساختار برنامه نویسی پروژه کار می‌کنم نیازی به ابزاری بود که بتونم جداول دیتابیس را با فیلدهای آن رسم و سپس ارتباط جداول را مشخص کنم و در نهایت بازبینی جداول، داکیومنت نویسی و توسعه پروژه بسیار راحت‌تر خواهد بود. از طرفی وقتی به صورت تیمی کار میکنیم درک این ساختار برای سایر اعضای تیم و افرادی که به تیم اضافه ‌می‌شوند بسیار راحتتر خواهد بود.

قبلا برای اینکار روی کاغذ جداول را رسم می‌کردم و ارتباط جداول را مشخص میکردم که بسیار زمانگیر بود تا اینکه تصمیم گرفتم از ابزارهایی برای اینکار استفاده کنم. به معرفی چند ابزار میپردازم و یکسری لینک آموزشی برای اینکار را بهتون معرفی میکنم.

۱. SQL Database Modeler

SQL Database Modeler

یک ابزار آنلاین است که نسخه بتا آن عرضه شده است و استفاده از آن به صورت رایگان است. شما میتوانید مدل و ساختار جداول را با استفاده از بخش داشبورد پروفایل ایجاد کنید.

model database

در نهایت بعد از ایجاد دیاگرام دیتابیس میتوانید از منوی سمت پایین گزینه Forward Engineer – Generate SQL را بزنید تا دیتابیس مورد نظر را اکسپورت کنید و در پروژه خود استفاده کنید. محدودیتی که این وبسایت دارد این است که فقط Microsoft SQL Server  و  MySql  را پشتیبانی می‌کند.

۲. MySQL Workbench

MySql

یکی از ابزارهای بسیار قدرتمند برای دیتابیس‌های MySql است که از طریق وب‌سایت رسمی mysql.com می‌توانید به صورت رایگان آن را دانلود نمائید. ( نسخه Enterprise هم دارد که امکانات بیشتری را در اختیارمون قرار میدهد. )

با استفاده از این ابزار بعد از اینکه کانکشن لازم را ایجاد کردیم، از قسمت Files > New Model  میتوانیم مدل دیتابیس موردنیاز پروژه را ایجاد کنیم و بعد از وارد کردن نام دیتابیس بر روی گزینه Add diagram کلیک میکنیم تا به محیط ایجاد دیاگرام جداول دیتابیس هدایت شویم.

MySql Workbench

بعد از ایجاد دیاگرام جداول و مشخص نمودن ارتباط جداول کافی است تا از قسمت Database گزینه Forward Engineer را انتخاب می‌کنیم تا دیتابیس ما طبق دیاگرام مورد نظر ساخته شود و میتوانیم از طریق همین محیط و یا محیط phpmyadmin به آن دسترسی داشته باشیم و جداول را مشاهده کنیم.

ویدئوهای آموزشی نصب و کار با Mysql Workbench :

Create MySQL Database – MySQL Workbench Tutorial

MySQL Workbench Tutorial

Introduction to MySQL Workbench

۳. Navicat

Navicat

یکی دیگر از ابزارهای بسیار قدرتمندی که میخواهم بهش اشاره کنم، Navicat است که متاسفانه رایگان نیست. علاوه بر MySql سایر دیتابیس‌ها را نیز پشتیانی می‌کند. به راحتی می‌توانید دیاگرام یا مدل دیتابیس پروژه موردنظرتان را ایجاد کنید.

Navicat

یکی از قابلیت‌های فوق‌ العاده این ابزار ارائه مدل مفهومی دیتابیس است که به درک ساختار پروژه بسیار کمک می‌کند. این مدل علاوه بر اینکه برای متخصص و برنامه نویس قابل درکه برای مالک پروژه که دانش فنی نداشته باشد نیز قابل درک است و این می‌تواند یک زبان مشترک بین متخصص و کارفرما برای پیشبرد بهتر پروژه باشد.

conceptual modeling db

در صورتیکه با زبان‌های مدلسازی بصری همچون UML آشنایی داشته باشید این ابزار این قسمت را نیز پشتیبانی می‌کند.

Design data model database

مشاهده ویدئوهای آموزشی Navicat

طراحی ریسپانسیو ( Responsive ) از ایده تا اجرا !

با شنیدن نام ریپسپانسیو ( responsive ) یا واکنش گرا این تصویر برای ما تداعی میشود که وب سایتی که واکنش‌گراست از طریق موبایل و سایر دیوایس‌ها در دسترس است، ولی واقعا چند درصد از این وب سایتهای ریسپانسیو قابل استفاده هستند؟!

یکی از مشکلات عمده اشخاصی که در حوزه UI/UX و فرانت‌اند کار می‌کنند پیاده سازی وبسایتی ریسپانسیو و کارا مطابق نظر مشتری یا صاحب پروژه است که با وجود عدم آشنایی با این مفهوم قصد دارد همه موارد داخل وبسایت که از طریق دسکتاپ قابل دسترس است روی موبایل و تبلت هم وجود داشته باشد!

قبلا در مقاله نوشته بودم که همیشه حق با مشتری نیست!

نکته : پروژه‌ای که ما برای کارفرما پیاده‌سازی می‌کنیم قطعا باید مورد پسند کارفرما باشد ولی این به این معنا نیست که هر چیزی که کارفرما درخواست داشت درست است، باید این اعتماد را ایجاد کنیم که کاری که انجام می‌شود بر اساس تجربه و دانشی است که در نهایت به نفع کارفرما تمام می‌شود و رابطه‌ی کاری برد-برد را مدنظر داریم در واقع این هنر توسعه دهنده است که چطور کارفرما را قانع کند.

پرسش و پاسخ برای درک یک واقعیت!

بیایید با طرح دو سوال به سبکی بهتر به موضوع بپردازیم :

  • چرا هنگام چک کردن و یا انتقال وجه از حساب بانکی خود از  وب‌سایت اصلی بانک موردنظر استفاده نمی کنید؟
  • چرا اپلیکیشن بانک مورد نظر را دانلود و از آن برای بررسی موجودی و …. استفاده می‌کنید؟

پاسخ ۱ : به این‌خاطر که فقط اطلاعات کاربردی و موردنیاز نمایش داده میشود و کار کردن با آن بسیار راحتتر از وب سایت برروی موبایل است.

پاسخ ۲ : به این خاطر که وب‌سایت فونت ریزی داره و اطلاعات اضافی زیادی وجود دارد.

پاسخ ۳ : سرعت بارگذاری پایین است، امکان انتخاب منو‌ها و آیتم‌ها سخت است و….

در واقع در نسخه ریسپانسیو فقط و فقط اطلاعاتی که موردنیاز کاربر است بر اساس نیاز نه سلیقه شخصی به نمایش بذاریم.

لازم است تاکید کنم هر پروژه ای بر اساس ماهیتی که دارد سبک اجرای آن متفاوت است، برای مثال وقتی پروژه مورد نظر ما یک وبلاگ است که درآمد این وبلاگ از تبلیغ هایی است که قرار می‌دهد قطعا ساختار طرح ریسپانسیو آن متفاوت خواهد بود با پروژه‌‌های دیگر.

یک سوال که اینجا مطرح می‌شود این است که این نیاز کاربران را از کجا تشخیص دهیم؟

 

مهمترین و بهترین راه تشخیص این نیاز آنالیز و تحلیل است. آنالیز و تحلیل رفتار کاربران روی وب‌سایت به ما این کمک را میکند که تشخیص بدهیم کاربر کجا را می‌بیند، کجا کلیک می‌کند و چگونه بین صفحات تعامل دارد.

در واقع ما وب‌سایت و پروژه را باید برای مشتری طراحی کنیم نه کارفرما! چونکه مشتری کارفرما رو به هدفی که می‌خواهد می‌رساند که این هدف می‌تواند پول و درآمد بیشتر باشد.

برای مثال یکی از دوستان من که در یکی از شرکت‌های بزرگ فروشگاهی فعالیت داشت، اظهار داشت که فروش محصولات فروشگاهی نسبت به بازدید صفحات محصول کمتر از مقدار مورد انتظار بود. بعد از تحلیل و بررسی رفتار کاربران و تغییر رنگ دکمه خرید و مکان دکمه خرید فروش محصولات را به میزان ۲۰ درصد افزایش دادند و این یعنی موفقیت در پروژه.

در نهایت لازم است تاکید کنم راه تشخیص اینکه کدام مورد بهتر و بهینه‌تر است تست و تحلیل نتایج است، یکی از راههایی که خیلی میتواند کمک کند A/B Testing است که مثلا به صورت رندم برای برخی کاربران استایل به یک سبکی نمایش داده شود و نتایج بررسی شود. این مورد مخصوصا در ایمیل مارکتینگ بسیار کاربردی و تاثیرگذار است که با استفاده از ابزارهایی همچون میل چیمپ و میلرلایت و … میتوان این موارد را تست کرد.

معرفی ابزارهایی برای آنالیز و تحلیل وب‌سایت و رفتار کاربران :

شما می‌توانید از ابزارهایی که وب‌سایت‌های زیر ارائه می‌دهند استفاده نمائید تا روند بهبود و رشد وب‌سایت خود را فرآهم کنید.

۱. گوگل آنالیتیک –  Google Analytics

۲. هات‌جار – Hotjar

۳. کریزی اِگ – Crazy Egg

در صورت تمایل می‌توانید کلمه Heat Map را در گوگل جستجو نمائید تا در رابطه با این موارد بیشتر مطالعه کنید.

 

نمایش لیستی از مکان‌‌ها بر روی نقشه و نمایش اطلاعات آنها

توی یکی از کارهایی که انجام می‌دادم نیاز شد که لیستی از مکانهای انتخابی ادمین سایت را روی نقشه نمایش دهم ( نمایش لیست مکان‌ها بر روی گوگل مپ ) به طوری که اگر کاربر روی یکی از اون موارد کلیک کرد اطلاعاتی از ان مکان را نمایش دهد از طرف دیگر لیستی از اون مکانها علاوه بر اینکه باید روی نقشه باشد باید در کنار نقشه نمایش داده شود :/

قبلا در رابطه با سفارشی کردن نقشه گوگل مپ مطلبی نوشته بودم که اگر فقط به این مورد نیاز دارید اون مطلب رو مطالعه کنید کافیه، ولی اگر مواردی که بالاتر اشاره کردم براتون جذابه ادامه مطلب رو بخونید 🙂

با استفاده از گوگل عزیز گشت و گذاری داشتم و مطالب زیادی رو پیدا کردم که کاربران نمونه کدها رو توی JSFiddle گذاشته بودند :

همه این موارد کاربرد خودشون رو دارند و از مورد اول میشه برای چیزی که مد نظر دارم استفاده کنم، اما پلاگینی نظرم رو به خودش جلب کرد که ترجیح دادم از اون استفاده کنم.

پلاگین MAPBOX :

MAPBOX Plugin

Mapbox is the location data platform for mobile and web applications. We provide building blocks to add location features like maps, search, and navigation into any experience you create.

We’re changing the way people move around cities and explore our world. Through the apps Mapbox powers, we reach more than 300 million people each month.

برای استفاده از این نقشه ابتدا باید توی وبسایت ثبت نام کنید و بعد Accrss Token  رو دریافت کنید تا بتونید توی پروژه از آن استفاده کنید.

ویژگی فوق العاده ای که داره می‌تونیم نقشه رو برای پروژه روی دیوایس‌های مختلف هم اینتگریت کنیم :

mapbox

قابلیت جالبی که داره می‌توانیم نقشه موردنظرمون رو به سبکی که دوست داریم دیزاین کنیم و کافیه توی کدها مسیر دیزاین مورد نظر را قرار دهیم تا از این دیزاین استفاده کنه.

design mapbox

همچنین میتوانید آمار بازدید نقشه‌هایی که ایجاد می‌کنید را مشاهده کنید.

mapbox statistics

استفاده از MapBox :

برای استفاده از این پلاگین مطابق مراحل این لینک پیش برید.

پس از قرار دادن کدهای کتابخانه mapbox از طریق کد زیر در صفحه پروژه میتونید نقشه mapbox رو فعال کنید.

mapboxgl.accessToken = 'کد توکن را اینجا قرار دهید.';
// This adds the map to your page
var map = new mapboxgl.Map({
  // container id specified in the HTML
  container: 'map',
  // style URL
  style: 'mapbox://styles/mapbox/light-v9',
  // initial position in [lon, lat] format
  center: [-77.034084, 38.909671],
  // initial zoom
  zoom: 14
});

نمایش لیست مکان‌ها :

برای نمایش لیست مکان‌ها به یک لیست با فرمت JSON نیاز داریم که داخل متغیری قرار گیرد تا این مقادیر را توی لایه قرار دهیم که روی نقشه قرار گیرند.

متغیری به نام stores تعریف میکنیم و لیست داده ها را داخل آن قرار میدهیم. ساختار آن به صورت تصویر زیر است که داخل لینکی که بالاتر قراردادم میتوانید لیست این داده ها را دانلود کنید و تست کنید.

JSON data list

بعد از تعریف متغیر و قرار دادن اطلاعات مکان‌ها با استفاده از کد زیر آنها را روی نقشه نمایش می‌دهیم.

map.on('load', function(e) {
  // Add the data to your map as a layer
  map.addLayer({
    id: 'locations',
    type: 'symbol',
    // Add a GeoJSON source containing place coordinates and information.
    source: {
      type: 'geojson',
      data: stores
    },
    layout: {
      'icon-image': 'restaurant-15',
      'icon-allow-overlap': true,
    }
  });
});

در صورتیکه تا اینجا قدم به قدم انجام داده باشید همچین دمویی دارید. دمو اولیه نقشه

first demo mapbox

قرار دادن لیست مکان‌ها در کنار نقشه :

در سایدبار کناری تگی با آی‌دی موردنظرمون ( listings ) قرار میدهیم. یعنی ساختار کدهای من تا به اینجای کار به صورت زیر است :

structure html code mapbox

تابع جاوااسکریپت زیر برای ایجاد لیست مکان‌ها را به کدهای اسکریپتی که نوشتیم اضافه میکنیم.

function buildLocationList(data) {
  // Iterate through the list of stores
  for (i = 0; i < data.features.length; i++) {
    var currentFeature = data.features[i];
    // Shorten data.feature.properties to just `prop` so we're not
    // writing this long form over and over again.
    var prop = currentFeature.properties;
    // Select the listing container in the HTML and append a div
    // with the class 'item' for each store
    var listings = document.getElementById('listings');
    var listing = listings.appendChild(document.createElement('div'));
    listing.className = 'item';
    listing.id = 'listing-' + i;

    // Create a new link with the class 'title' for each store
    // and fill it with the store address
    var link = listing.appendChild(document.createElement('a'));
    link.href = '#';
    link.className = 'title';
    link.dataPosition = i;
    link.innerHTML = prop.address;

    // Create a new div with the class 'details' for each store
    // and fill it with the city and phone number
    var details = listing.appendChild(document.createElement('div'));
    details.innerHTML = prop.city;
    if (prop.phone) {
      details.innerHTML += ' · ' + prop.phoneFormatted;
    }
  }
}

در واقع این فانکشن یک مقداری دریافت میکنه و با توجه به اون مقدار لیستی ایجاد میکنه و به تگ لیست ما اضافه میکنه. اگر متغیر stores رو یادتون باشه که داده‌ها رو به صورت JSON ذخیره کرده بود را باید به این فانکشن پاس بدیم. کافیه در کدهایی که ما لایه را ایجاد کردیم ( addLayer ) بعد از آن فانکشن را فراخوانی و مقدار sores را بهش پاس بدیم.

buildLocationList(stores);

تا به اینجای کار میتوانید دموی مورد نظرتون رو مشاهده کنید.

third demo mapbox - display list locations

نقشه را جذابتر و قابل تعاملتر کنیم!

کاری که میخواهیم انجام دهیم به این صورت است که وقتی بر روی لیست کناری نقشه کلیک شد، نقشه آن مکان را با فوکوس روی آن نمایش دهد.

دو تابع برای قابل تعامل کردن نقشه :

با کلیک کاربر بر روی لیست کنار نقشه ابتدا نقشه به مکان درست هدایت می‌شود و سپس یک پاپ آپ برای نمایش اطلاعات آن مکان نمایش داده می‌شود. هر دو تابع وقتی که کاربر بر روی یکی از مکانها کلیک میکند اجرا می‌شود.

function flyToStore(currentFeature) {
  map.flyTo({
    center: currentFeature.geometry.coordinates,
    zoom: 15
  });
}
function createPopUp(currentFeature) {
  var popUps = document.getElementsByClassName('mapboxgl-popup');
  // Check if there is already a popup on the map and if so, remove it
  if (popUps[0]) popUps[0].remove();
  var popup = new mapboxgl.Popup({ closeOnClick: false })
    .setLngLat(currentFeature.geometry.coordinates)
    .setHTML('<h3>Sweetgreen</h3>' +
'<h4>' + currentFeature.properties.address + '</h4>')
 .addTo(map);
}

میتوانید به پاپ‌آپی که بر روی نقشه نمایش داده می‌شود استایل دهید. بر روی این لینک کلیک کنید تا کدهای آن را مشاهده نمائید.

برای اینکه نقشه به درستی روی مرورگرهای قدیمی نیز کار کنه لازمه کد زیر رو در ابتدای اسکریپت اضافه کنید تا تابع remove() به درستی کار کند.

// This will let you use the .remove() function later on
if (!('remove' in Element.prototype)) {
  Element.prototype.remove = function() {
    if (this.parentNode) {
      this.parentNode.removeChild(this);
    }
  };
}

نکته  :  توابعی که تا اینجا معرفی شدند برای اینکه متغیرهای map, link شناسایی شوند باید داخل تابع buildLocationList که بالاتر این تابع رو اضافه کرده بودیم اضافه شوند.

پس از اینکه توابع فوق را اضافه کردیم باید آنها را فراخوانی کنیم. در دو حالت این فراخوانی ایجاد میشود، در حالت اول وقتی روی لینک سایدبار کلیک میشود و در حالت دوم وقتی روی آیکون رو نقشه کلیک میشود. در هر دوحالت حرکت نقشه و نمایش پاپ‌آپ را خواهیم داشت. پس ‌‌ Event های زیر را به انتهای تابع buildLocationList اضافه میکنیم.

وقتی که روی لینک سایدبار کلیک می‌شود :

// Add an event listener for the links in the sidebar listing
link.addEventListener('click', function(e) {
  // Update the currentFeature to the store associated with the clicked link
  var clickedListing = data.features[this.dataPosition];
  // ۱. Fly to the point associated with the clicked link
  flyToStore(clickedListing);
  // ۲. Close all other popups and display popup for clicked store
  createPopUp(clickedListing);
  // ۳. Highlight listing in sidebar (and remove highlight for all other listings)
  var activeItem = document.getElementsByClassName('active');
  if (activeItem[0]) {
    activeItem[0].classList.remove('active');
  }
  this.parentNode.classList.add('active');
});

وقتی روی آیکون روی نقشه کلیک می‌شود :

// Add an event listener for when a user clicks on the map
map.on('click', function(e) {
  // Query all the rendered points in the view
  var features = map.queryRenderedFeatures(e.point, { layers: ['locations'] });
  if (features.length) {
    var clickedPoint = features[0];
    // ۱. Fly to the point
    flyToStore(clickedPoint);
    // ۲. Close all other popups and display popup for clicked store
    createPopUp(clickedPoint);
    // ۳. Highlight listing in sidebar (and remove highlight for all other listings)
    var activeItem = document.getElementsByClassName('active');
    if (activeItem[0]) {
      activeItem[0].classList.remove('active');
    }
    // Find the index of the store.features that corresponds to the clickedPoint that fired the event listener
    var selectedFeature = clickedPoint.properties.address;

    for (var i = 0; i < stores.features.length; i++) {
      if (stores.features[i].properties.address === selectedFeature) {
        selectedFeatureIndex = i;
      }
    }
    // Select the correct list item using the found index and add the active class
    var listing = document.getElementById('listing-' + selectedFeatureIndex);
    listing.classList.add('active');
  }
});

نکته  :  addEventListenerی که خود داکیومنت سایت mapbox نوشته بود درست کار نمیکرد و با توجه به اینکه من توی پروژه از jQuery  استفاده کرده بودم به صورت زیر وقتی روی لینک کلیک میشود تابع را فراخوانی میکنم.

$('.item > a').on('click',function(){
                        
    // Update the currentFeature to the store associated with the clicked link
    var clickedListing = data.features[this.dataPosition];
    // ۱. Fly to the point associated with the clicked link
    flyToStore(clickedListing);
    // ۲. Close all other popups and display popup for clicked store
    createPopUp(clickedListing);
    // ۳. Highlight listing in sidebar (and remove highlight for all other listings)
    var activeItem = document.getElementsByClassName('active');
    if (activeItem[0]) {
        activeItem[0].classList.remove('active');
    }
    this.parentNode.classList.add('active');
    
});

سفارش کردن نهایی و تغییر مارکر ( آیکون روی نقشه ) :

تاکنون ما بر اساس استایلهایی که داشتیم و فانکشن‌هایی که معرفی کردیم نقشه را ایجاد کردیم و تمام بخش های آن واضح و روشن است. اکنون برای اینکه ما مارکر سفارشی خود را قرار دهیم. لازم است تغییراتی در کدهای بالا ایجاد کنیم.

اولین مورد باید لایه ای که در ابتدا ساختیم ( map.addLayer ) و مارکر را مشخص کردیم حذف کنیم و کد زیر را جایگزین کنیم.

  // This adds the data to the map
 map.on('load', function (e) {
 // This is where your '.addLayer()' used to be, instead add only the source without styling a layer
 map.addSource("places", {
 "type": "geojson",
 "data": stores
 });
 // Initialize the list
 buildLocationList(stores);
 });

همچنین EventListenerهایی که برای دو فانکشن نوشتیم را حذف میکنیم و با استفاده از آبجکت mapboxgl.Marker() کار را انجام می‌دهیم. داخل کد جدیدی که مینویسیم کلاس marker به آن اختصاص میدهیم پس به کدهای css خود کد زیر را اضافه میکنیم و آدرس مارکر موردنظرمون رو مشخص میکنیم.

.marker {
  border: none;
  cursor: pointer;
  height: 56px;
  width: 56px;
  background-image: url(marker.png);
  background-color: rgba(0, 0, 0, 0);
}

در نهایت کد زیر را اضافه میکنیم  که Event Listner جدید را نیز در آن قرار داده ایم :

  // This is where your interactions with the symbol layer used to be
  // Now you have interactions with DOM markers instead
  stores.features.forEach(function(marker, i) {
      // Create an img element for the marker
      var el = document.createElement('div');
      el.id  = "marker-" + i;
      el.className = 'marker';
      // Add markers to the map at all points
      new mapboxgl.Marker(el, {offset: [0, -23]})
          .setLngLat(marker.geometry.coordinates)
          .addTo(map);

      el.addEventListener('click', function(e){
          // ۱. Fly to the point
          flyToStore(marker);

          // ۲. Close all other popups and display popup for clicked store
          createPopUp(marker);

          // ۳. Highlight listing in sidebar (and remove highlight for all other listings)
          var activeItem = document.getElementsByClassName('active');

          e.stopPropagation();
          if (activeItem[0]) {
              activeItem[0].classList.remove('active');
          }

          var listing = document.getElementById('listing-' + i);
          listing.classList.add('active');
      });

  });

مشاهده دموی نهایی نقشه mapbox.

شما میتوانید آخرین ادیت کدهای اسکریپت فوق ( بخش آخر سفارشی شده ) را دانلود و بررسی کنید.

در نهایت من کدهای بالا را بر اساس نیاز خودم تغییر دادم و به تصویر زیر رسیدم، تغییراتی که ایجاد کردم به این صورت بود که با کلیک بر روی هر کدام از مکان ها علاوه بر نمایش اطلاعات، اطلاعات آن لیست در بالاترین قسمت لیست قرار داده میشود.

custom mapbox demo

شما می‌توانید روی گیت‌هاب من دموی کامل را مشاهده نمائید.

در نهایت لینک‌های زیر را برای استفاده بیشتر و مطالعه پیشنهاد میکنم:

 

ساختار کلی یک پروژه php

وقتی پروژه ای رو با php خام بدون هیچ فریمورکی ایجاد می‌کنیم قطعا باید ساختاری رو برای آن در نظر بگیریم که بتوان بعدها به راحتی به بخش‌های مختلف دسترسی داشت و برای توسعه آن هم راحتتر اقدام کنیم.

فایل‌های مختلفی همچون config.php , functions.php , actions.php , … را بر اساس سلیقه های شخصی می‌توان ساخت.

ساختاری که بسیاری از پروژه‌ها و فریمورک‌های معروف از آن استفاده می‌کنند ساختار MVC است که بر اساس Model , View , Controller این ساختار تعریف شده است. در مقاله ای دیگر در این رابطه خواهم نوشت ولی فعلا تا حد امکان سعی میکنم ساختار منظمی برای پروژه تعریف کنم و با شما به اشتراک بذارم.

مواردی که برای یک پروژه که با php خام ایجاد می‌شود را برای خودم توی این مقاله ذخیره میکنم.

فایل config پروژه :

فایلی را برای کانفیگ و تنظیمات کل پروژه در نظر می‌گیریم که در تمام بخش های پروژه در دسترس است.

۱ – ذخیره اطلاعات عمومی کل پروژه

اولین مورد ذخیره تمام اطلاعات کلی پروژه است که قرار است در کل پروژه استفاده شود. برای مثال نام کامل پروژه، آدرس دامنه وبسایت، تعداد مطالب/مقالات منتشر شده در هر صفحه و ….که من به صورت زیر این موارد را تعریف میکنم.

define('project_name', 'نام پروژه');
define('project_url', 'http://mekaeil.me'); 
...

۲ – مشخصات اکانت ادمین ( اختیاری )

برای هر پروژه معمولا یک مدیرکل وجود دارد که به همه بخش‌های وب‌سایت دارد. این نقش باید هنگام راه اندازی پروژه به صورت اتوماتیک ایجاد و در دیتابیس ثبت شود.

define('admin_username','mekaeil.me')
define('admin_email','[email protected]')

۳ –  فعال کردن نمایش خطاهای برنامه نویسی

قطعا در هنگام پیاده سازی پروژه نیاز داریم که خطاهایی که هنگام پیاده سازی و برنامه نویسی پروژه انجام می‌دهیم به ما نمایش داده شود. پس در ابتدای کار حتما این خطاها را نمایش می‌دهیم.

ini_set('display_errors','on');
error_reporting(E_ALL);

نکته مهم : هنگام اتمام پروژه و راه اندازی این موارد باید غیرفعال شوند.

۴ – اتصال به دیتابیس

برای دسترسی به دیتابیس لازم است اطلاعات اتصال به دیتابیس را در بخشی از پروژه قرار دهیم که بهترین بخش برای قراردادن این اطلاعات فایل config پروژه است.

$db_host = 'localhost';
$db_user = 'user_project';
$db_password = 'database password';
$db_name = 'database name';

با توجه به اینکه فایل کانفیگ در کل پروژه استفاده می‌شود میتوان اتصال به دیتابیس را هم در همین قسمت انجام داد به طوریکه لازم نباشد در هر قسمتی از پروژه این اتصال انجام شود.

همچنین اتصال دیتابیس را چک کنیم که در صورتی‌که به هر دلیلی دیتابیس وصل نبود پروژه نمایش داده نشود.

$db = new mysqli($db_host,$db_user,$db_password,$db_name);

// checking connection Database
if ( $db->connect_errno ) {
	print_r("connection Faild : ", $db->connect_error );
	exit();
}

۵ –  تنظیم یونیکد برای حروف فارسی

برای ثبت و نمایش درست اطلاعات فارسی لازم است یونیکد پروژه را روی utf-8تنظیم کنیم که داده‌ها به درستی ذخیره و نمایش داده شوند.

$db->query('SET NAMES UTF8;');

۶ – تعریف جدول‌های دیتابیس

در طول پروژه لازم است که queryهایی نوشته و اجرا شوند که باید به جداول مختلف دیتابیس برای اجرای این queryها نیاز داریم. برای اینکه راحتتر بتوانیم به آنها دسترسی داشته باشیم آنها را در فایل کانفیگ ست میکنیم. همانطور که مشاهده میکنید برای جداول users و posts  من نام آنها را تعریف کردم.

( نکته :  $db در کدهای بالاتر فایل کانفیگ به دیتابیس متصل شده است.  )

$db->usersTable = "users";
$db->postsTable = "posts";

فایل کانفیگ میتواند به مرور بر اساس پروژه کاملتر شود، بعد از ساخت فایل cconfig.php در قسمت هدر پروژه که در تمام بخش‌های پروژه تعریف شده است این فایل را به صورت  include_once ‘config.php’ فراخوانی می‌کنید، از این پس در تمام بخش ها به محتواهای این فایل دسترسی دارید.

فایل توابع پروژه php :

همانطور که در ساختار پروژه فایلی برای تنظیمات کل پروژه داریم قطعا باید فایلی برای توابع پر کاربرد و مورد استفاده در طول پروژه داشته باشیم. برای مثال در سیستم مدیریت محتوای وردپرس فایلی به نام functions.php  در پروژه استفاده می‌شود که تمامی توابع مورد نیاز را در آن قرار می‌دهیم.

در این فایل توابعی که در طول پروژه نیاز است تعریف می‌شوند و با تکمیل پروژه تکمیلتر خواهد شد.

برای مثال توابعی همچون is_login , create_user , update_post  و … را می‌توان در این فایل تعریف کرد.

فایلی برای مدیریت فرم‌ها و اکشن‌ها :

فایلی را با نامی دلخواه مثلا actions.php در نظر بگیریم که برای مقادیری که فرم‌ها ارسال می‌کنند مدیریت نماید. در واقع در این فایل چک می‌شود که آیا مقادیر این فرم ست شده است ( isset($_POST[‘input’]) ) که توابع داخل آن اجرا شود یا خیر.

بیشتر تعاملات کاربر با وب‌سایت معمولا از طریق فرم‌ها صورت میگیرد.

 

نکته ۱ : با توجه به اینکه تمام عملیات و کارهایی که انجام میدهیم نیاز به فایل actions دارند پس میتوانیم در قسمت head پروژه فایل actions  را include_once نمود و فایلهای config و functions  را در بالاترین  قسمت این فایل، به صورت include_once فراخوانی کنیم.

نکته ۲ : با توجه به موارد موفق تا به اینجای کار اگر بخواهیم مقایسه ای با ساختار MVC داشته باشیم، فایل functions.php که تعریف کردیم در واقع همان Model برای کار با دیتابیس، فایل actions.php همان Controller هستند و بخش View هم همانطور که می‌دانید بخش UI پروژه است.

نکته ۳ : با توجه به اینکه ما در پروژه خود از SESSION معمولا استفاده می‌کنیم پس لازم است در بالاترین قسمت فایل function حتما session_start() را قرار دهیم.

نکته ۴: می‌توان برای درخواست‌هایی که به صورت ‌Ajax ایجاد می‌شوند فایلی متمایز از actions را در نظر گرفت.

لازم است اشاره کنم در این مقاله به ساختار یک پروژه ساده بدون شی‌گرایی پرداختم که قطعا در حالت شی گرایی میتوان تغییرات زیادی اعمال کرد. در مقاله بعدی این مورد را بررسی میکنم.