ساخت وب سرویس با PHP و MyQSL و ارتباط آن با Retrofit
در این مبحث یک وب سرویس (API) ساده تحت PHP و دیتابیس MySQL مینویسیم و ارتباط آن با یک اپلیکیشن اندرویدی که شامل یک فرم ثبت نام و یک فرم ورود هست را بررسی میکنیم. در این پروژه از کتابخانه معروف Retrofit 2 برای ارتباط بین وب سرویس و کلاینت استفاده شده است.
این بخش شامل مباحث زیر میباشد:
- معرفی مختصر زبان PHP
- معرفی سیستم مدیریت پایگاه داده MySQL
- معرفی پنل phpMyAdmin جهت مدیریت دیتابیس MySQL
- نحوه کار با لوکال سرور WampServer
- نحوه ساخت دیتابیس و جداول در پنل phpMyAdmin
- ساخت یک وب سرویس ساده تحت زبان PHP و دیتابیس MySQL
- معرفی و تشریح تابعهای بکار رفته در سورس وب سرویس
- نحوه چاپ اطلاعات با فرمت JSON در PHP
- نحوه تست و خطایابی وب سرویس توسط نرم افزار Postman
- نحوه تست و خطایابی وب سرویس بدون نرم افزار و توسط URL
- اضافه کردن کتابخانههای Retrofit و Converter Gson به پروژه اندروید
- تعریف حق دسترسی اپلیکیشن به شبکه و اینترنت
- معرفی @SerializedName کتابخانه Gson
- استفاده از متدهای @POST و @GET برای ارسال و دریافت اطلاعات بین کلاینت و API
- ساخت فرم ثبت نام و ورود توسط Fragment
- جایگزین کردن فرگمنتها توسط متد replace
- مدیریت onResponse و onFailure در متد enqueue
این مبحث در قالب PDF و در ۴۷ صفحه تهیه شده که در ادامه چند صفحهی ابتدایی مربوط به بخشهای “وب سرویس” و “پروژه اندرویدی” را مشاهده میکنید:
به نام خدا. در مبحث گذشته با کتابخانه معروف Retrofit و همچنین فرمت انتقال داده JSON آشنا شدیم. پروژه جلسه قبل بسیار ساده بود و تنها شامل دریافت اطلاعات ثابت (استاتیک) از یک فایل json میشد.
در این جلسه قصد دارم یک وب سرویس (RESTful API) بسیار ساده و سبک با استفاده از زبان سمت سرور PHP و دیتابیس MySQL بسازم که قابلیت انتقال داده بین آن و یک کلاینت وجود داشته باشد. که در اینجا کلاینت ما یک دیوایس اندرویدی است.
سپس یک پروژه اندروید ایجاد میکنم که شامل یک فرم ثبتنام (رجیستر) و یک فرم ورود (لاگین) است.
نحوه کار نیز به این صورت است که با تکمیل فرم ثبتنام توسط کاربر، اطلاعات وارد شده توسط Retrofit و در بستر HTTP به وب سرویس ارسال شده و داخل دیتابیس ذخیره میگردد. همچنین در فرم لاگین، اطلاعاتی که کاربر وارد کرده (نام کاربری و رمز عبور) با اطلاعات ذخیره شده در دیتابیس مطابقت داده شده و در صورت صحت اطلاعات وارد شده، پیغام خوشآمد گویی حاوی نام و نام کاربری شخص نمایش داده میشود.
ساخت وب سرویس با استفاده از PHP و MySQL
PHP یک زبان برنامه نویسی شیءگراست که برای ساخت برنامههای سمت سرور استفاده میشود.
MySQL یک سیستم مدیریت پایگاه داده (دیتابیس) است که از طریق آن امکان ذخیره سازی، جستجو، مرتب کردن و بازیابی دادههای دیتابیس فراهم میگردد. در حال حاضر دیتابیس تعداد زیادی از وبسایتها توسط این سیستم مدیریت میشوند. در صورتی که قبلا با سرویسهای میزبانی سایت (هاستینگ) کار کردهاید و تجربه راه اندازی یک وب سایت را دارید (مانند یک وب سایت وردپرسی) احتمال زیاد با MySQL و پنل مدیریت phpMyAdmin آشنا هستید. MySQL هم مانند SQLite رایگان بوده که تحت مالکیت شرکت Oracle قرار دارد.
در اینجا قصد آموزش زبان PHP یا تشریح کامل MySQL را ندارم. اگر با کلیّت زبانهای برنامه نویسی آشنایی مختصری داشته باشید، کدهای PHP وب سرویس را به راحتی درک میکنید. با دستورهای زبان SQL هم قبلا در مبحث SQLite آشنا شدیم.
ابتدا WampServer را اجرا میکنم. در مبحث قبل با ومپ سرور آشنا شدیم و گفتیم که به واسطه این نرم افزار میتوانیم یک وب سرور محلی روی رایانه شخصی خودمان راه اندازی کنیم.
بعد از اجرای WampServer چند ثانیه زمان لازم است تا سرویسهای آن لود شوند. در صورتی که سرویسها به درستی لود شد، آیکون نرم افزار به رنگ سبز تغییر کرده و عبارت All Services Running را نشان میدهد:
اگر آیکون سبز نشد روی آن راست کلیک و گزینه Refresh را انتخاب کنید.
با کلیک روی آیکون ومپ سرور، منوی زیر باز میشود:
گزینه Localhost صفحه اصلی WampServer را در مرورگر باز میکند که اطلاعات مربوط به ماژولهای فعال را نشان میدهد. phpMyAdmin پنل مدیریت MySQL است که برای ساخت و مدیریت دیتابیس از این پنل استفاده میکنیم. در ادامه گزینههای دیگری برای مدیریت قسمتهای مختلف قرار دارد. در صورتی که هنوز آیکون نرم افزار سبز نشده یکبار گزینه Restart All Services را بزنید.
ابتدا باید یک دیتابیس بسازم. برای اینکار روی phpMyAdmin کلیک میکنم. صفحه ورود به پنل در مرورگر باز میشود:
در WampServer یا سایر Local Server ها، مطابق تصویر بالا Username برابر با root و Password باید خالی باشد. پس از ورود، پنل به صورت زیر باز میشود:
به قسمت Databases میروم و یک دیتابیس با نام دلخواه userdata ایجاد میکنم:
پس از ساخت دیتابیس یک table با نام دلخواه info به دیتابیس اضافه میکنم. من ۴ ستون برای این جدول نیاز دارم بنابراین در قسمت Number of columns عدد ۴ قرار میدهم. این چهار قسمت برای ذخیره اطلاعات کاربران است که شامل شناسه (id)، نام فرد (name)، نام کاربری (username) و رمز عبور (password) میشود.
سطر id را از نوع INT با اندازه ۵ و مابقی سطرها از نوع VARCHAR و اندازه ۵۰ درنظر گرفتم. id میبایست برای هر کاربر منحصر بفرد بوده و به صورت خودکار با ثبت نام هر کاربر یک واحد به آن اضافه شود بنابراین برای این سطر قسمت A I (مخفف AUTO INCREMENT) را تیک میزنم:
درباره Type و Length و سایر موارد، مفصل در مبحث SQLite بحث شده است و نیازی به تکرار نیست. در اینجا من ساخت جدول و ستون را در محیط ویژوال انجام دادم اما اینکار را با نوشتن Query هم میتوان انجام داد. روی Preview SQL کلیک کنید. کوئری زیر نشان داده میشود:
یعنی اگر نوشتن کوئری را بلد باشیم با قرار دادن آن در قسمت SQL نیز میتوانیم جدول را ایجاد کنیم:
بهرحال هردو روش قابل انجام است. من در همان مرحله قبل یعنی محیط ویژوال بعد از ورود اطلاعات مربوط به سطرها، گزینه Save را میزنم و ستونها ساخته میشود:
خب! فعلا با دیتابیس کاری ندارم.
در فولدر www مربوط به WampServer یک فولدر با نام دلخواه api میسازم و فایلهایی که قبلا آماده کردهام را به اینجا منتقل میکنم:
من این وب سرویس ساده را با زبان PHP نوشتهام بنابراین پسوند فایلها php. است. کدهای مربوط به هر فایل را به صورت خلاصه توضیح میدهم تا با کلیت نحوه کارکرد این وب سرویس آشنا شوید. برای باز کردن فایلها از یک ادیتور ساده و رایگان مانند ++Notepad یا Sublime Text استفاده کنید (سورس وب سرویس در فولدر Exercises قرار دارد).
db_connect.php:
در این فایل اطلاعات مربوط به دیتابیس را وارد کردهام و سپس دستور اتصال به دیتابیس نوشته شده:
چهار متغیر با نامهای دلخواه ساختهام که اطلاعات دیتابیس را شامل میشود. دیتابیس روی همین سیستم میزبانی میشود بنابراین مقدار آن localhost است. user و password همان یوزر و پسورد ورود به phpMyAdmin است. یعنی root برای user و رشته خالی “” برای password. نام دیتابیس را هم درون متغیر dbname ذخیره کردم. حتما میدانید نامگذاری متغیرها مانند آنچه در جاوا قبلا آشنا شدهاید، دلخواه بوده و قانونی وجود ندارد.
در ادامه توسط تابع mysqli_connect() یک کانکشن برای دیتابیس ایجاد میکنم. این تابع ۴ مقدار ورودی گرفته که همان اطلاعاتی است که در ابتدای فایل تعریف شده. برای آنکه مطمئن شوم دستور اتصال را به درستی نوشتهام و ارتباط با دیتابیس برقرار است، یک if() نوشتم که اتصال یا عدم اتصال را بررسی میکند. اگر $connection برقرار باشد (یعنی mysqli_connect با اطلاعاتی که گرفته توانسته به دیتابیس متصل شود) پیغامی مبنی بر اتصال موفق و در غیر اینصورت پیغام شکست در اتصال چاپ میشود. برای بررسی نتیجه کار کافیست فایل را روی مرورگر اجرا کنم:
پیغام اول چاپ شد یعنی در اتصال به دیتابیس مشکلی وجود ندارد. پس از حصول اطمینان از صحت عملکرد اتصال به دیتابیس، دستور if را حذف یا Comment کنید تا از خروجی نهایی وب سرویس حذف شود:
بعد از غیر فعال شدن این دستور، با فراخوانی مجدد فایل در مرورگر نباید هیچ خروجی چاپ شود:
در ابتدای جلسه تصمیم گرفتیم اپلیکیشن ما یک فرم ثبت نام و یک فرم ورود داشته باشد. بنابراین دو فایل جداگانه برای مدیریت این قسمتها به وب سرویس اضافه شده.
register.php:
ابتدا توسط دستور require فایل db_connect.php فراخوانی شده است زیرا برای دریافت اطلاعات کاربر و ثبت داخل دیتابیس نیاز به اتصال به دیتابیس داریم. در فرم ثبت نام، اطلاعات شامل نام، نام کاربری و رمز عبور از کاربر دریافت میشود. سه متغیر $name، $username و $password توسط متد $_GET دادههای ارسالی از سمت کلاینت را دریافت و درون خود ذخیره میکنند.
در فرایند ثبت نامِ کاربر باید بررسی شود نام کاربری که انتخاب کرده قبلا توسط شخص دیگری انتخاب نشده باشد. بنابراین یک متغیر با نام $sql_select نوشتم که مقدار آن یک کوئری از نوع SELECT است:
SELECT * FROM info WHERE username = '$username'
این دستور (کوئری) جدول info درون دیتابیس را بررسی میکند و اگر در ستون username مقداری برابر با مقدار ذخیره شده در متغیر $username که از کلاینت (فرم ثبت نام) دریافت شده وجود داشته باشد آنرا برمیگرداند.
در متغیر $result نیز تابع mysqli_query() ابتدا به دیتابیس متصل میشود ($connection)، سپس دستور فوق را به دیتابیس ارسال میکند. در نهایت آنچه از این کوئری حاصل میشود (یعنی وجود یا عدم وجود username مشابه با آنچه کاربر فعلی قصد ثبت نام دارد) در این متغیر ذخیره میشود.
سپس یک شرط داریم. تابع mysqli_num_rows() در PHP تعداد سطرهای برگشت شده از کوئری را اعلام میکند (num مخفف number به معنی تعداد و rows به معنی سطرها).
mysqli_num_rows($result) > ۰
اگر username ای که شخص وارد کرده قبلا توسط کاربر دیگری انتخاب شده باشد، $result آنرا برمیگرداند پس mysqli_num_rows() بزرگتر از ۰ خواهد بود و در نتیجه قسمت اول شرط اجرا میشود:
$status = "REGISTERED";
یعنی رشته متنی REGISTERED درون متغیر $status ذخیره میگردد.
ولی اگر کاربری قبلا با این نام کاربری ثبت نام نکرده باشد، mysqli_num_rows($result) حاصلش ۰ است بنابراین قسمت دوم شرط اجرا میشود. یعنی کد درون بدنه else.
در خط ۱۵ یک کوئری با نام $sql_insert تعریف کردم. این کوئری، مقادیر ذخیره شده در متغیرهای $name، $username و $password را درون جدول info و درسطرهای name، username و password وارد میکند. برای اینکه مطمئن شویم اطلاعات به درستی درون دیتابیس ذخیره شده یا نه، یک شرط بعد از این کوئری اضافه کردم که اگر کوئری به دیتابیس ارسال شده، عبارت SUCCESS و در غیر اینصورت عبارت ERROR در متغیر $status ذخیره شود.
پس تا اینجا متغیر $status میتوانید سه وضعیت را در خود ذخیره کند:
– نام کاربری قبلا استفاده شده و پیغام REGISTERED را برمیگرداند.
– نام کاربری آزاد است، عملیات ثبت نام با موفقیت انجام میشود و اطلاعات کاربر درون دیتابیس ذخیره میگردد و در نهایت SUCCESS را برمیگرداند.
– نام کاربری آزاد است ولی به دلایلی اطلاعات دریافتی روی دیتابیس ثبت نمیشود که ERROR را برمیگرداند.
در نهایت توسط تابع json_encode() مقدار ذخیره شده در $status در قالب یک آبجکت JSON چاپ (echo) میشود:
echo json_encode(array("response"=>$status));
خروجی این خط یک آبجکت JSON است که یک جفت نام/مقدار دارد. من نام آنرا response تعیین کردم. مقدار نیز همان $status است.
در انتها کانکشنی که به دیتابیس زده شده توسط تابع mysqli_close() بسته میشود زیرا تا عملیات بعدی به این کانکشن نیازی نداریم.
login.php:
عملیات مرتبط با لاگین کاربری که قبلا ثبت نام کرده از طریق این فایل انجام میپذیرد. ابتدا username و password ای که کاربر در فرم لاگین وارد کرده، به سرور ارسال و در متغیرهای $username و $password ذخیره میشود. سپس یک کوئری از نوع SELECT نوشتم که username و password ارسالی از سمت کلاینت را در جدول info دیتابیس جستجو میکند و در صورتی که این نام کاربری و رمز عبور در دیتابیس موجود باشد، نام (name) را برمیگرداند.
تابع $result نقشی مشابه تابع موجود در register.php دارد.
سپس یک شرط تعیین شده که اگر حاصل mysqli_num_rows($result) بزرگتر از صفر بود، توسط تابع mysqli_fetch_assoc() مقدار name از سطر مربوطه در دیتابیس درون متغیر $name ذخیره گردد. به علاوه اینکه عبارت SUCCESS به عنوان status ثبت شود. در ادامه، وضعیت (status)، نام کاربر (name) و نام کاربری وی (username) با فرمت JSON چاپ شود.
اگر حاصل mysqli_num_rows($result) برابر با ۰ باشد، وضعیت FAILED چاپ میشود.
خب! کارِ نوشتن وب سرویس تمام شد. قبل از اینکه سراغ اندروید استودیو بروم، ابتدا میخواهم وب سرویس را تست کنم تا مطمئن شوم عملیات ذخیره و بازیابی دادهها به درستی انجام میپذیرد و فرمت خروجی JSON آن صحیح و بدون نقص است.
(جهت مطالعه ادامه توضیحات وب سرویس، فایل PDF را دانلود کنید)
ساخت پروژه اندروید اتصال به وب سرویس توسط کتابخانه Retrofit
سراغ اندروید استودیو میروم و یک پروژه با نام Retrofit & PHP و اکتیویتی از نوع Empty Activity میسازم.
اولین قدم، اضافه کردن کتابخانههای Retrofit به build.gradle است:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' }
یادتان نرود برای اتصال به شبکه و اینترنت حتما حق دسترسی لازم را باید به مانیفست اضافه کنیم.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ir.android_studio.retrofitphp"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
دادههایی که از وب سرویس دریافت میکنیم response (مشترک در هر دو فرم) و همچنین name و username برای فرم ورود هستند. بنابراین یک کلاس میسازم و این سه پارامتر را تعریف میکنم. من نام این کلاس را User میگذارم:
سه متغیر داخل کلاس تعریف کردم. سپس متدهای Getter را میسازم (alt+insert)
نامگذاریهای فوق با نامهایی که در وب سرویس انتخاب کرده بودم متفاوت است. بنابراین لازم است تعیین کنیم هر متغیر مربوط به کدام مورد است. برای اینکار از annotation با نام @SerializedName استفاده میکنیم که مربوط به کتابخانه Gson است:
User.java:
package ir.android_studio.retrofitphp; import com.google.gson.annotations.SerializedName; public class User { @SerializedName("response") private String apiResposnse; @SerializedName("name") private String apiName; @SerializedName("username") private String apiUsername; public String getApiResposnse() { return apiResposnse; } public String getApiName() { return apiName; } public String getApiUsername() { return apiUsername; } }
شاید بپرسید خب چرا کار را سخت کنیم؟ میشد متغیرها را همنام با پارامترهای موجود در وب سرویس تعریف کرد تا نیاز به این عمل اضافی نباشد (مانند مبحث گذشته). انتقاد شما بجاست. اما ممکن است روال نامگذاری توسعه دهنده با آنچه از قبل در وب سرویس تعریف شده (و امکان تغییر سورس وب سرویس میسر نیست) متفاوت باشد و برنامه نویس مصمم به نامگذاری به شیوه خودش باشد. در اینجا این راهکار را کتابخانه Gson در اختیار ما قرار داده است.
برای ساخت Retrofit یک کلاس جدید با نام دلخواه API ساختم. کد آن به صورت زیر است.
API.java:
package ir.android_studio.retrofitphp; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class API { public static final String BASE_URL = "http://192.168.56.1/api/"; public static Retrofit myRetrofit = null; public static Retrofit getAPI() { if (myRetrofit == null) { myRetrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); } return myRetrofit; } }
مانند مبحث قبل ابتدا URL مربوط به وب سرویس را تعریف کردم. سپس یک شرط تعیین شده تا بررسی کند اگر Retrofit برابر null بود آنگاه Retrofit.Builder() را بسازد (اگر null نباشد یعنی قبلا ساخته شده).
نوبت ساخت Interface است. مانند جلسه قبل یک کلاس جدید میسازم و نوع آنرا بجای Class از نوع Interface تعیین میکنم. من نام ApiInterface را روی این فایل گذاشتم.
ApiInterface.java:
package ir.android_studio.retrofitphp; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Query; public interface ApiInterface { @POST("register.php") Call<User> regCall(@Query("name") String Name, @Query("username") String UserName, @Query("password") String Password); @GET("login.php") Call<User> loginCall(@Query("username") String UserName, @Query("password") String Password); }
در پروژه مبحث قبل فقط یک متد @GET داشتیم اما اینجا یک متد @POST هم برای بخش عضویت کاربر استفاده میشود که دادههای کاربر باید به سمت سرور ارسال شود. البته همانطور که در قسمت کار با Postman اشاره شد، در هردو قسمت (یعنی ثبت نام و ورود) میتوان از @GET استفاده کرد چون ما مستقیما از کوئریها استفاده میکنیم. با اینحال از @POST هم استفاده کردم تا برای شما ناآشنا نباشد.
جهت مطالعه ادامه آموزش، فایل PDF را دانلود نمائید
توجه : سورس پروژه اندروید و وب سرویس درون پوشه Exercises قرار دارد
با توجه به اینکه آموزشهای پایه با قیمت پایین در اختیار کاربر قرار گرفته و درآمد حاصل صرف تامین هزینههای وب سایت و تهیه آموزشهای آتی میشود، به اشتراک گذاری این فایل با دیگران خلاف اخلاق است.
تعداد صفحات : ۴۷
حجم : ۳٫۵ مگابایت
قیمت : ۳۶ هزار تومان
توجه: صرفا در صورتی از درگاه پشتیبان استفاده کنید که قادر به پرداخت از طریق سبد دانلود نباشید.
افزودن به سبد دانلود درگاه پشتیبان
سلام و درود بر شما دوست عزیز
میدونم سوالم مربوط به این بحث نمیشه ولی تقریبا چند ماهی هست که به این مشکل تو اندروید برخوردم و نمیتونم هیچ کاری بکنم و کل اینترنت رو هم زیرورو کردم و نتیجه ای نداشت.
من میخوام با استفاده از برنامه نویسی سوکت یک پیغام به سروری که با php نوشته شده بفرستم و بقیه ی کاربران اون پیغام رو دریافت کنن.
میخوام بازی آنلاین چندنفره بنویسم که بازیکن ها باهم ارتباط داشته باشن، بازی آنلاین بسیار ساده
خواهش میکنم راهنمایی کنین لطفا…
سلام خسته نباشید من موقعی که برنامه wampserver نصب کردم گزینه های مربوط به android studio و wp را ندارم , مشکل از کجاست؟
ممنون میشم راهنمایی کنین.
لطفا آموزش رو با دقت مطالعه کنید. پوشه ها رو خودتون و با نام دلخواه خودتون باید ایجاد کنید
سلام. من بعد از ثبت مشخصات کاربر هیچ پیغامی به صورت توست دریافت نمیکنم و اروری هم نمیگیرم و چیزی هم به دیتابیس اضافه نمیشه. این آی پی که در BASE_URL تعریف کردید مختص سیستم شماست یا برای ما هم همینه؟ از wrampservice استفاده میکنم و در مسیر دیفالت نصبش کردم.
در لوکال معمولا آی پی یکسان هست اما بازم باید مطمئن بشید درست وارد کردید. روش بررسی صحت url در آموزش موجود هست
سلام و وقت بخیر.
من کل پروژه رو اجرا کردم و با این خطا داخل دیوایس مواجه شدم:
Connection Error!com.google.gson.stream.MalformedJsonExcetion:
$Use JsonReader.setLeneint(true) to accept malformed JSON at line 1 column 1 path
داخل دیتابیس ایجاد میشه اما پیغام success نمیاره
پرسش و پاسخ لینک زیر رو مطالعه کنید. مشکل شما هم دلیلش باید همین باشه
https://stackoverflow.com/q/69857621
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import com.google.gson.annotations.SerializedName;
رو خطا میگیره اندروید استودیو . چیکار کنم؟
https://stackoverflow.com/q/35767319
سلام استاد وقتتون بخیر
برای قسمت ورود به اپلیکیشن وقتی با وب سرویس ورود رو زدیم چطور دفعه بعد که کاربر اپلیکیشن رو باز کرد همین طور ورود باشه و مجدد نخواد که ورود بزنه ؟
با تشکر
این مورد خارج از مبحث وب سرویس هست
سلام
من وقتی روی شبیه ساز برنامه رو ران میکنم کاملا درست کار میکنه
اما وقتی روی دستگاه اندروید تست میکنم ارور network security میده
آموزشتون عالی بود لطفا آموزش های بیشتری درباره اتصال به سرور بذارین
سلام
من هاست و دامنه خریدم و فایل های php شمارو داخل هاستم اپلود کردم وقتی میزنم کدinsert کار نمیکنه و هر چی مقدار میدم چیزی ثبت نمیشه
کد رو مال خودتون گذاشتم پس فکر نکنین اشتباه تایپ شده اموزش هم مثل مال شما رفتم
ممکنه درستش کنین هر جور شده خیلی بهش نیاز دارم
فک کنم نیاز باشه عکسی ازش بفرستم که خودتونم ببینین ولی دیدگاه قابلیت عکس نداره اگه امکانش هس تلگرام بمن پیام بدین که این مشکلم حل بشه واقعاااا ممنون میشم
@Angel9088