Questionnaire web app – Node JS and Mongo DB

A questionnaire web app is a website that allows users to answer questions based on the best of their knowledge in a limited time. The user who answered the most questions right comes 1st, then 2nd, and 3rd. Users also have the capability to ban another user. The user having the highest negative votes will be banned from that contest and will appear in the next contest.

Demo:

We offer 2 versions of this project. One is free and has fewer functions, and one is pro and has all the functions.

Download demo version:

Local storage Swift UI – CRUD

Local storage means the data that is stored inside the app. You do not need an internet connection to create or read data from local storage. We will create a simple iOS app in Swift UI that allows you to create, read, update, and delete values in local storage. For that, we are going to use a built-in class in Swift called UserDefaults.

Note: UserDefaults class must only be used to store singular values, for example, last_login, name, email, etc. It should NOT be used to store data in the tabular form like MySQL, or in embedded form like Mongo DB.

Local Storage

We will create a simple Swift file named LocalStorage.swift. This file will handle the creating, fetching, updating and deleting the data from local storage.

//
//  LocalStorage.swift
//  Local storage CRUD
//
//  Created by Adnan Afzal on 16/11/2020.
//  Copyright © 2020 Adnan Afzal. All rights reserved.
//

import Foundation

class LocalStorage {
    
    private static let myKey: String = "myKey"
    
    public static var myValue: String {
        set {
            UserDefaults.standard.set(newValue, forKey: myKey)
        }
        get {
            return UserDefaults.standard.string(forKey: myKey) ?? ""
        }
    }
    
    public static func removeValue() {
        UserDefaults.standard.removeObject(forKey: myKey)
    }
    
}

  1. We are creating a static constant named myKey which will hold the name of data.
  2. Then we are creating a static variable named myValue and it has getter and setter. They will automatically be called when we try to save or fetch the value from it.
  3. UserDefaults.standard.string this function returns an optional value, so we have to tell the default value using ?? “”. Here we are setting empty value as default.
  4. Lastly, a static function named removeValue() is created which will remove that value from UserDefaults.

We have made the variables and functions static, so we can use them without creating an object of the LocalStorage class.

You can use the same technique to add as more values as required.

Main Swift UI view

First, we will create a view where we will show 4 buttons to create, view, update, and delete data from UserDefaults. In your main ContentView file, create a navigation view, and inside it create 4 navigation links horizontally.

Right now, we are creating navigation link for AddView only. We will create navigation links for others later in this article.

NavigationView {
    HStack { 
        NavigationLink (destination: AddView(), label: {
            Text("Add")
        })
    }
}
  1. destination: is the view which will be displayed when this navigation link is clicked
  2. label: is the view that will be displayed to the user.

Add value to local storage

Now create a new Swift UI view file named AddView.swift. In this view, we will create an input field and a button which when clicked will save the text field value in local storage.

To get the value from the text field in Swift UI, we will use the @State property wrapper. First, create a @State variable of type String inside your AddView struct:

@State var value: String = ""

Now we can bind this variable in text field’s text value.

VStack {
    TextField("Enter value", text: $value)

    Button(action: {
        LocalStorage.myValue = self.value
    }, label: {
        Text("Save")
    })
}
  1. VStack will show the views vertically.
  2. TextField field is created and its value is bound with our value variable.
  3. Button is created with the text “Save” which when clicked will set the myValue variable from LocalStorage class to our value variable.

The setter inside LocalStorage myValue variable will automatically be called when we assign the value. The code inside setter saves the value in UserDefaults.

Get value from local storage

First, we will create a navigation link in our main view to move to a new view to show value from local storage.

// ContentView.swift

NavigationLink (destination: DataView(), label: {
    Text("View")
})

Now create a new Swift UI view file named DataView.swift. This file will simply fetch the value and show it in text view.

Text(LocalStorage.myValue)

This will automatically called the getter inside LocalStorage myValue variable. The getter is fetching the value from UserDefaults. If the value is not found, then it is setting the default value to empty string.

Edit value in local storage

First, we will create a navigation link in our main view to move to a new view to update the value in local storage.

// ContentView.swift

NavigationLink (destination: EditView(), label: {
    Text("View")
})

Now create a new Swift UI view file named EditView.swift. Editing the value is almost similar to adding the value. But it has 2 steps:

  1. Show the existing value in text field.
  2. Update the value from text field.

Again, we will create a @State property wrapper in this view:

@State var value: String = ""

Then, we will create VStack to show text field and a button vertically. Bound the value variable to text field’s value. Create a button with a text “Update” and setting the local storage value to text field value (same as we did for adding value).

But when the view is loaded, you need to show the existing value in the text field. You can do that by adding an onAppear function to your main view (VStack in my case). In the perform parameter, we will fetch the value from local storage and assign it to our value variable.

As the text field’s text is bound to value variable, so when it’s value is changed, the text field’s text will automatically be changed.

VStack {
    TextField("Enter value", text: $value)
    Button(action: {
        LocalStorage.myValue = self.value
    }, label: {
        Text("Update")
    })
}
.onAppear(perform: {
    self.value = LocalStorage.myValue
})

Delete a value from local storage

We do not need to create a navigation link to delete. We will simply create a button that when clicked will call the function removeValue() from LocalStorage class.

Button(action: {
    LocalStorage.removeValue()
}, label: {
    Text("Delete")
})

Your local storage CRUD operation in Swift UI is done here.

[wpdm_package id=’870′]

Send value from one view to another – Swift UI

We will create a text field in one view and send its value into another view. We will be taking an example of a search, you write a text to search in one view and the second view will show you the text you had written on the first view.

Note: When implementing a complete search functionality, you usually show the searched data based on a text query. But this is just an example of how you can implement this.

Video tutorial:

@State property wrapper

We will create 2 @State property wrappers, one to save the value from the text field and the second to show the search page when the user hits the return key.

// string variable to store search query
@State var searchedText: String = ""

// boolean variable to show search view
@State var showSearch: Bool = false

Text field

Now we will create a text field and binds its value to our first variable. As the user types in the input field, we will have its value in that variable.

TextField("Search here...", text: $searchedText, onCommit: {
    // this will be called when the search is clicked
    self.showSearch = true
})
.padding(8)
.keyboardType(.webSearch)
.background(Color(.systemGray6))
.disableAutocorrection(true)
.cornerRadius(5)

Following are the things which needs to understand from above code:

  1. TextField’s first parameter is the string that will be shown to the user.
  2. The second parameter is the binding which will store the text field value in the given variable.
  3. onCommit: is called when the user hits the return key. In this callback, we are setting the showSearch value to true. Note: We will come back to this later.
  4. padding(8): sets the margin between text content and text field borders.
  5. keyboardType(.webSearch): will show a full keyboard with a search icon on bottom right.
  6. background(Color(.systemGray6)): will set the background color of text field to light gray.
  7. disableAutocorrection(true): will disable the auto-correction on this field.
  8. cornerRadius(5): this will make the corners of the text field a little rounded rather than squares.

Pass value to next view

Now we will pass this text field value to next view. This can be done using navigation link:

// goto search view
NavigationLink (destination: SearchView(searchedText: $searchedText), isActive: $showSearch) {
    EmptyView()
}

destination is the view which will be shown when the value in isActive becomes true.

Right now, you will get an error in SearchView because we haven’t created that view yet. Also, we are sending searchedText as a parameter which means that we have to create a @Binding property wrapper in search view.

The EmptyView() is the label that is shown in the first view. But we don’t want any text to be displayed on the initial value, so we are making it an empty view.

When using navigation link, you must wrap your outer parent with navigation view. So your view body will be like this:

NavigationView {
    VStack {
        
        // goto search view
        NavigationLink (destination: SearchView(searchedText: $searchedText), isActive: $showSearch) {
            EmptyView()
        }
        
        TextField("Search here...", text: $searchedText, onCommit: {
            // this will be called when the search is clicked
            self.showSearch = true
        })
        .padding(8)
        .keyboardType(.webSearch)
        .background(Color(.systemGray6))
        .disableAutocorrection(true)
        .cornerRadius(5)
        
    }.padding()
}

Search view

Now, create another view file in your project named SearchView.swift. As we are sending a searched text in its constructor, so we must add a @Binding variable in it:

// @Binding is used when variables need to be passed between 2 views
@Binding var searchedText: String

Now, you might get the error in your preview provider code. It is because we need to pass the value from here as well. Replace your preview provider body code with the following:

// when using @Binding, @State static must be used to show preview
@State static var searchedText: String = ""

static var previews: some View {
    SearchView(searchedText: $searchedText)
}

Now, inside the body of view, we are simply displaying the searched value in a text. But you can use it in any way you want.

// show searched text in this view
        
VStack {
    Text(self.searchedText)
}

That’s how you can send value from one view to another using Swift UI.

[wpdm_package id=’865′]

Show an alert with callback Swift UI

To show an alert with callback in Swift UI, you need 3 things:

  1. A boolean state variable that tells when to show the alert message.
  2. A string state variable that tells the title of the alert.
  3. A string state variable that tells the content of an alert message.

Video tutorial:

So let’s go ahead and create these 3 variables in your view:

@State var showAlert: Bool = false
@State var alertTitle: String = ""
@State var alertMessage: String = ""

When the variable becomes true, an alert will be displayed with a title and a message.

// main layout
VStack {

    // when clicked will show the alert
    Button(action: {
        
        // set title of alert
        self.alertTitle = "Error"

        // set message of alert
        self.alertMessage = "Please try again"
        
        // this line will actually show the alert
        self.showAlert = true
        
    }, label: {
        // text to show in button
        Text("Show alert")
    })
    
    // bind showAlert here
}.alert(isPresented: $showAlert, content: {

    // alert dialog
    Alert(
        title: Text(self.alertTitle),
        message: Text(self.alertMessage),
        dismissButton: .default(Text("OK")) {

            // this will be called when the dismiss button is clicked
            print("dismissed")
        }
    )
})

Show API data in XCode preview – Swift UI

You won’t be able to preview your layout in your XCode preview in Swift UI when you are receiving data from the server. So we are going to show you how you can preview the layout with the data from your API.

Video tutorial:

API to return JSON data

First, create an API that will return the JSON in a pretty format. We are going to create that API in PHP. You can find the sample database in the attachment below.

<?php

    $conn = mysqli_connect("localhost:8889", "root", "root", "classicmodels");

    $result = mysqli_query($conn, "SELECT * FROM customers");

    $data = array();
    while ($row = mysqli_fetch_object($result))
    {
        array_push($data, $row);
    }

    header("Content-Type: application/json");

    echo json_encode($data, JSON_PRETTY_PRINT);

?>

It does the following things:

  1. Connect with the database.
  2. Fetch records from the customer’s table.
  3. Create a new empty array.
  4. Loop through all records from the database.
  5. Push in the array.
  6. Return the response as JSON pretty format.

If you run this file in your browser using XAMPP, WAMP, or MAMP, you will see all the data in JSON format. Copy all the content from the browser.

Create a JSON file in the XCode project

Now create a new file in your XCode project named “data.json”, you must select “Empty” while creating a new file in your XCode project. And paste the copied content into that file.

Create model class

Now in your View where you want to show the data, first create a model class that will hold the values you want to show in your List view.

// ResponseModel.swift

class ResponseModel: Codable, Identifiable {
    var customerName: String? = ""
    var country: String? = ""
}

Model class must implement Codable and Identifiable protocols.

Create an array of models

Inside your View class, create an array of these models and initialize it as an empty array.

@State var models: [ResponseModel] = []

List view

Now create a List view and show these 2 variables from the model class in the Text view.

VStack {
    List (models) { (model) in
        Text(model.customerName ?? "").bold()
        Text(model.country ?? "")
    }
}

This is the syntax to create a list view from an array. We are making the customer name to bold so we can differentiate between the customer name and country.

The layout could be different as per your need.

Read JSON file and Decode it into the model

Now we need to fill this “models” array with data from the JSON file we saved in our project. Following is the code that will read the JSON file from the XCode project and decode the JSON data into an array of models.

guard let url: URL = Bundle.main.url(forResource: "data", withExtension: "json") else {
    print("data.json not found")
    return
}

do {
    
    let data: Data = try Data(contentsOf: url)
    
    self.models = try JSONDecoder().decode([ResponseModel].self, from: data)
    
} catch {
    print(error.localizedDescription)
}

It does the following:

  1. Get the file from the bundle named “data.json”. The guard statement will throw an error if the file is not found. The bundle will return a URL object.
  2. Create a do-catch block that will throw an error if the data is not decodable to ResponseModel class.
  3. Convert the URL into a Data object.
  4. Decode the Data object into an array of ResponseModel class and assign it to the “models” array.

As “models” is a @State variable so it will automatically render the List view when the data is added to the model’s array.

Render List view

You can run the above inside your VStack’s onAppear function like below:

VStack {
    List (models) { (model) in
        Text(model.customerName ?? "").bold()
        Text(model.country ?? "")
    }
}.onAppear(perform: {

    // above code here

});

Conclusion

This is really helpful because when you are developing an app, you have to preview the layout you are designing in XCode, instead of re-running the app again and again to see the output.

You can do this with every API whose data you want to show in your Swift UI app. So that’s how you can show your API data in your XCode preview.

Learn how to create a search bar with history in Swift UI and SQLite.

Search bar Swift UI with history – SQLite

[wpdm_package id=’866′]

Get data from API and show in list – Swift UI, PHP

We will create a simple iOS app in Swift UI that will call an API request, developed in PHP, to the HTTP server. The API will return an array of records in JSON format, which we will decode in Swift class objects as an array. Then we will display that list in a list view.

If you prefer a video tutorial:

Our API will be in PHP and we will call it from our Swift UI app. So, create a file in your htdocs (for XAMPP), or www (for MAMP or WAMP) folder and paste the following code in it. We have created a folder named “test” and inside it, the file name is “swiftui-get-data-from-api.php”:

<?php

    $conn = mysqli_connect("localhost:8889", "root", "root", "classicmodels");

    // get data from database
    $result = mysqli_query($conn, "SELECT * FROM customers");

    // adding in array
    $data = array();
    while ($row = mysqli_fetch_object($result))
    {
        array_push($data, $row);
    }

    // send as response
    echo json_encode($data);

?>

For that, you need to create a database named “classicmodels” in your phpMyAdmin and import the SQL file attached below. You need to create and login into your account in order to download that file. You can see the output from the link:

http://localhost/test/swiftui-get-data-from-api.php

This might be changed and it depends on the path of your file. You will see a lot of data, for example, we want to show only customerName and country in our Swift UI app. So you need to create a model file in your project using XCode and paste the following code into it:

// ResponseModel.swift
class ResponseModel: Codable, Identifiable {
    var customerName: String? = ""
    var country: String? = ""
}

Now in your View file where you want to show the list, you can paste the following code:

@State var models: [ResponseModel] = []
    
var body: some View {
    // Create VStack
    VStack {
        // now show in list
        // to show in list, model class must be identifiable
        
        List (self.models) { (model) in
            HStack {
                // they are optional
                Text(model.customerName ?? "").bold()
                Text(model.country ?? "")
            }
        }
        
    }.onAppear(perform: {
        // send request to server
        
        guard let url: URL = URL(string: "http://localhost:8888/test/swiftui-get-data-from-api.php") else {
            print("invalid URL")
            return
        }
        
        var urlRequest: URLRequest = URLRequest(url: url)
        urlRequest.httpMethod = "GET"
        URLSession.shared.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
            // check if response is okay
            
            guard let data = data else {
                print("invalid response")
                return
            }
            
            // convert JSON response into class model as an array
            do {
                self.models = try JSONDecoder().decode([ResponseModel].self, from: data)
            } catch {
                print(error.localizedDescription)
            }
            
        }).resume()
    })
}

All the code is self-explanatory and comments are also added to explain difficult lines. But if you still face any issues in following this tutorial, please let us know.

[wpdm_package id=’847′]

Pick image from gallery and upload to server – SwiftUI and PHP

Learn how to upload image from SwiftUI app. We will not be using any external library in this tutorial. All code is done in native SwiftUI. We will use a picker from an iPhone gallery and upload it to your server. We will be using SwiftUI for developing the app and PHP for uploading the image to the server.

First, you need to create a file in your project named ImagePicker.swift and paste the following content in it:

//
//  ImagePicker.swift
//
//  Created by Adnan Afzal on 30/10/2020.
//  Copyright © 2020 Adnan Afzal. All rights reserved.
//

import Foundation
import SwiftUI

extension View {
    public func asUIImage() -> UIImage {
        let controller = UIHostingController(rootView: self)
        
        controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
        UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
        
        let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        controller.view.sizeToFit()
        
        // here is the call to the function that converts UIView to UIImage: `.asImage()`
        let image = controller.view.asUIImage()
        controller.view.removeFromSuperview()
        return image
    }
}

extension UIView {
// This is the function to convert UIView to UIImage
    public func asUIImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

struct ImagePicker: UIViewControllerRepresentable {

    @Environment(\.presentationMode)
    var presentationMode

    @Binding var image: Image?

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

        @Binding var presentationMode: PresentationMode
        @Binding var image: Image?

        init(presentationMode: Binding<PresentationMode>, image: Binding<Image?>) {
            _presentationMode = presentationMode
            _image = image
        }

        func imagePickerController(_ picker: UIImagePickerController,
                                   didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
            image = Image(uiImage: uiImage)
            presentationMode.dismiss()

        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            presentationMode.dismiss()
        }

    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(presentationMode: presentationMode, image: $image)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {

    }

}

Now we have created a video tutorial to help you done this task. You can follow the video below:

[wpdm_package id=’867′]

Logged in devices management – PHP & MySQL

In this article, we will be creating logged in devices management feature that will allow the users to check how many devices they have been logged in. You might have seen this feature in Facebook where you can see a list of all devices where you have been logged in, and you also have the capability to remove device. The devices which has been removed will no longer be logged in and had to be logged in by entering the email and password again.

Following are the steps to manage multiple devices/browser logins:

  • When user enter correct email and password during login, we will check if current device and browser is trusted by that user.
  • If it is not trusted, then it will send an email with a verification code to user’s email address and an input field is displayed.
  • User have to enter the verification code in that input field.
  • Once the code is verified, then the user will be asked to trust this device/browser or not.
  • If user trusts the device, then the next time he tried to login, he won’t be asked for a verification code.
  • A separate page is created to show a list of all devices where user is logged in.
  • From that page, user can remove the device he wants.
  • When the device/browser is removed, then if the user tried to login from that device/browser, then a new verification code will be sent again on his email address.

Table of content:

  1. Database structure
  2. Login form
  3. Login form submission
  4. Verification code
  5. Trust device
  6. Show all logged in devices
  7. Remove device

1. Database structure

First we will create 2 tables, one for users and one for devices. You might already have a table for users, if you do, make sure to add a new column verification_code in it. If you already have a table for users, you can add the verification_code column by running the following query:

/* if you already have the users table */
ALTER TABLE users ADD COLUMN verification_code TEXT NOT NULL

If you don’t have the users table, then run the following query in your phpMyAdmin or you can create the tables manually.

CREATE TABLE IF NOT EXISTS users(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    email TEXT NOT NULL,
    password TEXT NOT NULL,
    verification_code TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS devices(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    user_id INTEGER NOT NULL,
    browser_info TEXT NOT NULL,
    browser_token TEXT NOT NULL,
    last_login DATETIME NOT NULL,
    last_login_location TEXT NOT NULL,
    
    CONSTRAINT fk_devices_user_id FOREIGN KEY (user_id) REFERENCES users(id)
);
  • user_id will be a foreign key from users table.
  • browser_info will store the browser and operating system name e.g. Safari Mac OS X.
  • browser_token this will be stored as a cookie in browser to identify the browser.
  • last_login this will tell the time when that device was last logged in.
  • last_login_location this will tell the user’s location from where the login happened.
  • fk_devices_user_id is the name of constraint we use to set the primary key of ID from users table as a foreign key in devices table.

2. Login form

<form method="POST">

    <table>
        <tr>
            <td>
                Email
            </td>
            <td>
                <input type="email" name="email">
            </td>
        </tr>

        <tr>
            <td>
                Password
            </td>

            <td>
                <input type="password" name="password">
            </td>
        </tr>
    </table>
 
    <input type="submit" value="Login" name="login">
</form>

This will create an email and password field along with a submit button labeled as “Login”. You might already have one similar to this, actual change will be in the next step.

3. Login form submission

When the form is submitted, we will check it’s credentials and make sure they are right. Then we will check if this device or browser is trusted by user, if not, then we will send an email to the user to verify this device. If yes, then he will be redirected to devices page where he can see all his devices.

To send an email, we are going to use PHPMailer, you can learn how to integrate PHPMailer by following this.

<?php

// session start is required for login
session_start();

// connecting with database
$conn = mysqli_connect("localhost", "db_username", "db_userpassword", "db_name");

// check if the form is submitted
if (isset($_POST["login"]))
{
    // get input field values, preventing from SQL injection
    $email = mysqli_real_escape_string($conn, $_POST["email"]);
    $password = mysqli_real_escape_string($conn, $_POST["password"]);
      
    // check if the email exists in database
    $sql = "SELECT * FROM users WHERE email = '" . $email . "'";
    $result = mysqli_query($conn, $sql);
 
    if (mysqli_num_rows($result) == 0)
    {
        echo "In-correct email";
        exit();
    }
    else
    {
        // check if the password is correct, we are using hashed password
        $row = mysqli_fetch_object($result);
        if (password_verify($password, $row->password))
        {
            // store the user in session
            $_SESSION["user"] = $row;

            // check if the device or browser is trusted or not
            $sql = "SELECT * FROM devices WHERE browser_token = '" . $_COOKIE["browser_token"] . "' AND user_id = '" . $row->id . "'";
            $result = mysqli_query($conn, $sql);

            // device/browser is trusted
            if (mysqli_num_rows($result) > 0)
            {
                header("Location: devices.php");
            }
            else
            {

                // not trusted, send an email.
                // generate a unique verification code
                $verification_code = uniqid();

                // Instantiation and passing `true` enables exceptions
                $mail = new PHPMailer(true);

                try {
                    //Server settings
                    $mail->SMTPDebug = 0;                      // Enable verbose debug output
                    $mail->isSMTP();                                            // Send using SMTP
                    $mail->Host       = 'smtp.gmail.com';                    // Set the SMTP server to send through
                    $mail->SMTPAuth   = true;                                   // Enable SMTP authentication
                    $mail->Username   = 'your_email';                     // SMTP username
                    $mail->Password   = 'your_password';                               // SMTP password
                    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;         // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
                    $mail->Port       = 587;                                    // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above

                    //Recipients
                    $mail->setFrom('your_email', 'your_name');
                    $mail->addAddress($email);

                    // Content
                    $mail->isHTML(true);                                  // Set email format to HTML
                    $mail->Subject = 'Verify this browser';
                    $mail->Body    = 'Your verification code is <b style="font-size: 30px;">' . $verification_code . '</b>';

                    $mail->send();
                    // echo 'Message has been sent';
                } catch (Exception $e) {
                    die("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
                }

                // save the verification code in users table
                $sql = "UPDATE users SET verification_code = '" . $verification_code . "' WHERE id = '" . $_SESSION["user"]->id . "'";
                mysqli_query($conn, $sql);

                ?>

                <!-- show a form to enter verification code from email -->

                <h1>New device detected. Email has been sent with a verification code.</h1>

                <form method="POST">

                    <table>
                        <tr>
                            <td>
                                Verification code
                            </td>
                            <td>
                                <!-- verification code input field -->
                                <input type="text" name="verification_code">
                            </td>
                        </tr>
                    </table>
                 
                    <!-- submit button -->
                    <input type="submit" value="Verify" name="verify">
                </form>

                <?php
            }
        }
        else
        {
            echo "Invalid password";
            exit();
        }
    }
}

Once the email is sent, you will see the verification code field value in that user’s row in database. You will also see an input field labeled as “Enter verification code”, here you need to enter the coode received in your email address.

Note: If you do not receive an email, make sure you have entered correct email and password in PHPMailer and also your Gmail’s account less secure apps option should be enabled. You can enable it from here.

4. Verification code

Now when the verification form is submitted, we will do the following:

  • Check if the verification code entered in form matches with the one in database.
  • If matches, then we will empty the verification code field from users table.
  • And show a form to trust this device/browser or not.
<?php

if (isset($_POST["verify"]))
{
    $user_id = isset($_SESSION["user"]) ? $_SESSION["user"]->id : 0;
    $verification_code = $_POST["verification_code"];

    $sql = "SELECT * FROM users WHERE id = '" . $user_id . "' AND verification_code = '" . $verification_code . "'";
    $result = mysqli_query($conn, $sql);

    if (mysqli_num_rows($result) == 0)
    {
        die("Verification code has been expired.");
    }

    $sql = "UPDATE users SET verification_code = '' WHERE id = '" . $user_id . "' AND verification_code = '" . $verification_code . "'";
    mysqli_query($conn, $sql);

?>

    <form method="POST">
        <input type="button" onclick="window.location.href = 'devices.php';" value="Don't trust this device">
        <input type="submit" name="trust_device" value="Trust device">
    </form>

<?php
}

If user presses “Don’t trust this device”, then user will again receive the verification code next time he tried to login in this browser. We are simply redirecting the user to devices page where he will see all his logged in devices.

5. Trust device

Now if the user presses the button to “Trust device”, then we will do the following:

  • Generate a unique ID, store it in browser’s cookies.
  • Get browser info (browser name, device type and platform). You need to uncomment browscap line in your php.ini to use this feature.
  • Get user’s IP address to get his location using Geo plugin. If it is not working from localhost, then you need to place your IP address manually, you can get your IP address from Google.
  • From Geo plugin, we are getting country and city name.
  • Finally we will insert that data in devices table, thus next time you will login with same device, it will not ask for verification code.

So our logged in devices management feature will keep track of all trusted devices.

<?php

if (isset($_POST["trust_device"]))
{
    $browser_token = uniqid();
    setcookie("browser_token", $browser_token);

    $browser = get_browser(null, true);
    $browser_info = $browser["browser"] . " " . $browser["device_type"] . " " . $browser["platform"];

    $user_ip = getenv('REMOTE_ADDR');

    $geo = unserialize(file_get_contents("http://www.geoplugin.net/php.gp?ip=$user_ip"));
    $country = $geo["geoplugin_countryName"];
    $city = $geo["geoplugin_city"];
    $last_login_location = $country . ", " . $city;

    $sql = "INSERT INTO devices (user_id, browser_info, browser_token, last_login, last_login_location) VALUES ('" . $_SESSION["user"]->id . "', '" . $browser_info . "', '" . $browser_token . "', NOW(), '" . $last_login_location . "')";
    mysqli_query($conn, $sql);

    header("Location: devices.php");
}

?>

You will be redirected to file named “devices.php“. Now we need to show all logged in devices to user so he can remove if he want.

6. Show all devices

Create a file named devices.php and paste the following code in it:

<?php

// devices.php

// session start is required for login
session_start();

// connecting with database
$conn = mysqli_connect("localhost", "db_username", "db_userpassword", "db_name");

// check if the user is logged in
if (!isset($_SESSION["user"]))
{
    die("Not logged in");
}

// paste the remove device code here from next step

// get all devices of logged in user
$sql = "SELECT * FROM devices WHERE user_id = '" . $_SESSION["user"]->id . "'";
$result = mysqli_query($conn, $sql);

?>

Apply some CSS to make the table look good.

table, th, td {
    border: 1px solid black;
    border-collapse: collapse;
}
th, td {
    padding: 15px;
}

Show all devices data in tabular form:

<!-- table to show all devices data -->
<table>
    <tr>
        <th>Device info</th>
        <th>Last login</th>
        <th>Last location</th>
        <th>Actions</th>
    </tr>

    <!--  table row for each device -->
    <?php while ($row = mysqli_fetch_object($result)): ?>
        <tr>
            <td><?php echo $row->browser_info; ?></td>

            <!-- last login date in readable format -->
            <td><?php echo date("d M, Y H:i:s A", strtotime($row->last_login)); ?></td>
            <td><?php echo $row->last_login_location; ?></td>
            <td>
                <!-- form to remove the device -->
                <form method="POST">
                    <input type="hidden" name="id" value="<?php echo $row->id; ?>">
                    <input type="submit" name="remove_device" value="Remove device">
                </form>
            </td>
        </tr>
    <?php endwhile; ?>
</table>

The code is self-explanatory in comments. Now we need to add a function to remove device. We have already displayed a form with a submit button which when clicked should remove the device from user’s logged in devices list and should not allow that device to login without verification.

7. Remove device

Now we will simply remove the selected device from devices table for logged in user:

<?php

// check if form is submitted
if (isset($_POST["remove_device"]))
{
    // get device ID
    $id = $_POST["id"];

    // remove from database
    $sql = "DELETE FROM devices WHERE user_id = '" . $_SESSION["user"]->id . "' AND id = '" . $id . "'";
    mysqli_query($conn, $sql);

    // success message
    echo "Device has been removed.";
}

?>

So you have successfully created your logged in devices management feature. Try integrating it in one of your existing project. And let us know if you face any problem.

[wpdm_package id=’832′]

Admin roles in admin panel – PHP & MySQL

Let’s say you have an admin panel of your website where you can manage your website’s data. Now you want to have a functionality where you can create sub-admins with access to limited features. For example, one admin can manage posts (add, edit and delete), another admin can manage customers, another admin can manage employees and so on. And they all will be managed by super admin. So we need to assign different admin roles to each admin.

In this article, we will be creating a sub admin to manage posts.

Create database table

Create a table for admins where we will have a column named “roles”, it’s type will be ENUM so you can specify the roles. No roles other than specified in ENUM will be accepted.

CREATE TABLE `admins` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `email` text NOT NULL,
  `password` text NOT NULL,
  `role` enum('all','manage_posts') NOT NULL
);

CREATE TABLE `posts` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `title` text NOT NULL,
  `created_by` int(11) NOT NULL,
  CONSTRAINT `fk_created_by_posts` FOREIGN KEY (`created_by`) REFERENCES `admins` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
);

Insert data in MySQL database

Super admin will be created manually and for once only. Give the role “all” to super admin:

INSERT INTO `admins` (`id`, `email`, `password`, `role`) VALUES
(1, 'admin@gmail.com', '$2y$10$e0qHrQw8irU1TPxjzfB2OOAQ/uUH/xq5jAP58f796jMAOLwEv2d9i', 'all')

Admin login form

You can generate password hash from here. First we will create a login form for all admins:

<?php
    // start session and connect with database
    session_start();
    $conn = mysqli_connect("localhost", "root", "root", "tutorials");
?>

<!-- check if admin is logged in -->
<?php if (isset($_SESSION["admin"])): ?>
    <!-- button to logout -->
    <p>
        <a href="?logout">Logout</a>
    </p>
<?php else: ?>
    <!-- form to login -->
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <input type="submit" name="login" value="Login">
        </p>
    </form>
<?php endif; ?>

This will show a button to logout if the admin is logged in and a login form if an admin is not logged in.

Log in the admin and start his session

Now we will write the code to login the admin and start his session:

// check if request is for login
if (isset($_POST["login"]))
{
    // get email and password
    $email = $_POST["email"];
    $password = $_POST["password"];

    // check if email exists
    $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
    if (mysqli_num_rows($result) > 0)
    {
        // check if password is correct
        $admin = mysqli_fetch_object($result);
        if (password_verify($password, $admin->password))
        {
            // start session
            $_SESSION["admin"] = $admin;
            echo "<p>Logged in.</p>";
        }
        else
        {
            echo "<p>Wrong password.</p>";
        }
    }
    else
    {
        echo "<p>Email not found.</p>";
    }
}

This will first check if the email exists in the database. Then it will compare the hashed password with plain text from input field. If credentials are okay then it will save the admin object in session variable.

Logout admin

Now you will see the logout button.

// check if request is for logout
if (isset($_GET["logout"]))
{
    // remove from session and redirect back
    unset($_SESSION["admin"]);
    header("Location: " . $_SERVER["HTTP_REFERER"]);
}

When the logout button is clicked, we will remove this admin object from session variable and redirect the admin to the page where he came from. There are other methods to redirect the user to previous page and you will check those methods from here.

Add sub-admin form

Now if the logged-in admin is super admin then we will show him a form to add a new admin:

<!-- check if main admin -->
<?php if ($_SESSION["admin"]->role == "all"): ?>

    <!-- add admin form -->
    <h1>Add admin</h1>
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <label>Enter role</label>
            <select name="role" required>
                <option value="all">All</option>
                <option value="manage_posts">Manage posts</option>
            </select>
        </p>

        <p>
            <input type="submit" name="add_admin" value="Add admin">
        </p>
    </form>
<?php endif; ?>

This will ask for the admin’s email and password along with the role that you want to assign to him.

INSERT sub-admin in MySQL database

Now we will write the code to save his data in the database:

// check if request is for adding admin
if (isset($_POST["add_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get values
        $email = $_POST["email"];
        $password = password_hash($_POST["password"], PASSWORD_DEFAULT);
        $role = $_POST["role"];

        // check if email already exists
        $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
        if (mysqli_num_rows($result) > 0)
        {
            echo "<p>Email already exists.</p>";
        }
        else
        {
            // save in database
            mysqli_query($conn, "INSERT INTO admins (email, password, role) VALUES ('" . $email . "', '" . $password . "', '" . $role . "')");
            echo "<p>Admin has been added.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

First, this will check that the logged-in admin must have an access to create sub-admin. Then it will get all the fields, it also converts the plain text password into hashed string. Then it checks if an admin with same email already exists, if not then it saves the data in database and display a success message.

Show all sub-admins to super admin

Now we need to show all sub-admins to super admin so he can know all his sub-admins along with their roles, and also an ability to delete any sub-admin. Below code should be written after the “Add admin” form:

<?php
    // show all admins
    $all_admins = mysqli_query($conn, "SELECT * FROM admins WHERE id != '" . $_SESSION["admin"]->id . "'");
    while ($admin = mysqli_fetch_object($all_admins)):
?>
    <p>
        <?php echo $admin->email . " - " . $admin->role; ?>

        <!-- button to delete admin -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $admin->id; ?>">
            <input type="submit" name="delete_admin" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will show all sub-admins to super admin only. When the delete button is clicked, it will first ask for confirmation. When confirm, it will submit the form. Now we need to handle the form submission in PHP:

// check if request is for deleting admin
if (isset($_POST["delete_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get value
        $id = $_POST["id"];

        // delete from database
        mysqli_query($conn, "DELETE FROM admins WHERE id = '" . $id . "'");
        echo "<p>Admin has been deleted.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

This will simply check that the logged-in admin must be a super admin. Then it will delete the admin from database. When a sub-admin is deleted, all his created posts will also be deleted as well. If you want the sub-admin posts to stay after his removal, you need to remove the “ON DELETE CASCADE ON UPDATE CASCADE” clause from “posts” table during creation.

Sub-admins

Now we come to the sub-admin part. Sub admins can perform action based on their roles. For example, sub admin having role “manage_posts” can create, edit and delete posts. First we will create a form to add post:

<!-- check if admin has permission to manage posts -->
<?php if ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"): ?>
    <!-- form to add new post -->
    <h1>Add post</h1>
    <form method="POST">
        <p>
            <input type="text" name="title" placeholder="Enter title" required>
        </p>

        <p>
            <input type="submit" name="add_post" value="Add post">
        </p>
    </form>
<?php endif; ?>

This will check that the logged-in admin must either be a super admin or admin having role “manage_posts”. Now we need to handle its request in PHP:

// check if request is for adding post
if (isset($_POST["add_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get values
        $title = $_POST["title"];
        $created_by = $_SESSION["admin"]->id;

        // save in database
        mysqli_query($conn, "INSERT INTO posts (title, created_by) VALUES ('" . $title . "', '" . $created_by . "')");
        echo "<p>Post has been added.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

We need to validate the sub-admin role in server side as well. Get all fields from input fields, and logged in admin ID so we can know which sub-admin created that post. Then we will insert the data in database.

Now we need to show all posts of sub-admin created by him so he can perform further actions like updating or deleting post.

<?php
    // get all posts
    $all_posts = mysqli_query($conn, "SELECT * FROM posts WHERE created_by = '" . $_SESSION["admin"]->id . "'");
    while ($post = mysqli_fetch_object($all_posts)):
?>
    <p>
        <?php echo $post->title; ?>

        <!-- button to delete post -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $post->id; ?>">
            <input type="submit" name="delete_post" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will fetch all posts created by logged-in admin from database and display their titles along with a button to delete. When the delete form is submitted, it will ask for confirmation, once confirm it will submit the form. Now we need to handle the form request on server side:

// check if request is for deleting post
if (isset($_POST["delete_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get value
        $id = $_POST["id"];

        // check if post is created by logged in admin
        $result = mysqli_query($conn, "SELECT * FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
        if (mysqli_num_rows($result) == 0)
        {
            echo "<p>Sorry you cannot perform this action.</p>";
        }
        else
        {
            // delete from database
            mysqli_query($conn, "DELETE FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
            echo "<p>Post has been deleted.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

Again it will check if the logged-in admin is either super admin or has a role to manage posts. Additionally, it will also check if the post he is trying to delete is created by him. That’s how it will be secured. If all validations are passed then it will simply delete the post from database.

That’s how you can create multiple admin roles and assign the roles by admin capability.

[wpdm_package id=’833′]

phpMyAdmin for FTP or SFTP – Laravel

In our previous post, we gave a detailed explanation of why you need a database viewer if you are working with only FTP or SFTP. In this post, we will create a phpMyAdmin for developers who have access to FTP or SFTP only while working on a Laravel project.

Let’s get started

First, you need to create a separate route and a separate controller. Following will be your routes, paste them into your routes/web.php file:

use App\Http\Controllers\PHPMyAdminController;

Route::prefix("/phpmyadmin")->group(function () {

    Route::get("/", [PHPMyAdminController::class, "index"]);
    // all other routes will reside here

});

Connect with SSH

Now we need to create a controller, and run the following command in your Terminal or Git bash. Now if you are working via FTP then you might only have SSH access, if you do not know how to connect with your server via SSH, please follow the below tutorial first:

Creating a controller

After you have connected with SSH, open your project root folder in your command prompt and run the following command:

php artisan make:controller PHPMyAdminController

This will create an empty controller file in your app/Http/Controllers/PHPMyAdminController folder.

use DB;
use Schema;

class PHPMyAdminController extends Controller
{

}

Install “dbal” library from composer

Now we need to fetch all tables. You need to run the following command first:

composer require doctrine/dbal

This command will install a library that helps to get all table names in the connected database. Paste the following lines in your PHPMyAdminController file:

public function index(Request $request)
{
    $tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();

    return view("phpmyadmin.tables", [
        "tables" => $tables
    ]);
}

Code at line #3 will fetch all the tables from the connected database. We have created a separate folder named resources/views/phpmyadmin, we will place all our views files here.

View all tables in MySQL database

First, create a file named tables.blade.php and display all tables using the following code:

<!-- tables.blade.php -->

<table>
    <thead>

        <tr>
            <th>name</th>
            <th></th>
        </tr>

    </thead>
    <tbody>

        @foreach ($tables as $table)
            <tr>
                <td>{{ $table }}</td>
                <td>
                    <div>
                        <a href="{{ url()->to('/phpmyadmin/browse-table/' . $table) }}">
                            Browse
                        </a>
                    </div>
                </td>
            </tr>
        @endforeach

    </tbody>
</table>

You can design it as per your desire. It will display all table names along with the button “Browse”. On click it will redirect to the browse-table route we will create in the next step.

View table data

Create the following route in your “phpmyadmin” group in web.php:

Route::get("/browse-table/{name}/{search?}", [PHPMyAdminController::class, "browse_table"]);

It has a name required parameter which tells the name of table whose data you want to view. Search parameter is optional, it will only be used when performing the search functionality. Any get parameter that you want to make optional in Laravel should have question mark (?) at the end. Now create its function in your controller:

public function browse_table(Request $request, $name, $search = "")
{
    $columns = Schema::getColumnListing($name);

    if (empty($search))
    {
        $data = DB::table($name)->paginate();
    }
    else
    {
        $query = DB::table($name);
        foreach ($columns as $column)
        {
            $query = $query->orWhere($column, "LIKE", "%" . $search . "%");
        }
        $data = $query->paginate();
    }

    return view("phpmyadmin.browse-table", [
        "name" => $name,
        "search" => $search,
        "data" => $data,
        "columns" => $columns
    ]);
}

First it gets all columns of selected table. Then it checks if the search query is made, right now the if condition will be true and code at line #7 will be executed.

It will get few records from that table and with pagination, which means that it can display a pagination to view more records. Finally we are sending that data in our browse-table view. Create a file named browse-table.blade.php and view all rows of selected table:

<!-- browse-table.blade.php -->

<table>
    <thead>
        <tr>
            @foreach ($columns as $column)
                <th>{{ $column }}</th>
            @endforeach
        </tr>
    </thead>

    <tbody>

        @foreach ($data as $d)
            <tr>
                @foreach ($columns as $column)
                    <td>{{ $d->{$column} }}</td>
                @endforeach
                <td>
                    <a href="{{ url()->to('/phpmyadmin/edit_row/' . $name . '/' . $d->id) }}">
                        Edit
                    </a>
                    
                    <form method="POST" action="{{ url()->to('/phpmyadmin/delete_row') }}" onsubmit="return confirm('Are you sure you want to delete this row ?');">
                        {{ csrf_field() }}

                        <input type="hidden" name="name" value="{{ $name }}">
                        <input type="hidden" name="id" value="{{ $d->id }}">

                        <button class="item" type="submit">
                            <i class="zmdi zmdi-delete"></i>
                        </button>
                    </form>
                </td>
            </tr>
        @endforeach

    </tbody>
</table>

{{ $data->links() }}

This will display all columns in table header. In tbody, first it will loop through all rows then it will display each column value. Last column will have 2 buttons, one to edit the and one to delete the row. To add rows, we will be using Laravel seeders. When the delete button is clicked, we will show a confirmation dialog, if user presses “okay” then we will delete the row.

Delete row

Create the following route in your routes/web.php file inside phpmyadmin group:

Route::post("/delete_row", [PHPMyAdminController::class, "delete_row"]);

Now create a function in your controller class that will delete that row using its ID from the selected table.

public function delete_row(Request $request)
{
    DB::table($request->get("name"))->where("id", "=", $request->get("id"))->delete();
    return redirect()->back()->with("danger", "Record has been deleted");
}

Edit row

After that, we come to the editing part. Create a route which will fetch the selected row for edit and display it in input fields:

Route::get("/edit_row/{name}/{id}", [PHPMyAdminController::class, "edit_row"]);

Then create a function in your controller class to fetch the row and its columns and render it in our new blade template:

public function edit_row(Request $request, $name, $id)
{
    $data = DB::table($name)->where("id", "=", $id)->first();
    if ($data == null)
    {
        return redirect()->back()->with("error", "Record does not exists.");
    }
    $columns = Schema::getColumnListing($name);
    return view("phpmyadmin.edit-row", [
        "data" => $data,
        "name" => $name,
        "id" => $id,
        "columns" => $columns
    ]);
}

Make sure you added use Schema; at the top of your controller class. Now create a blade template file named “edit-row.blade.php” in our resources/views/phpmyadmin folder.

<form action="{{ url()->to('/phpmyadmin/edit_row') }}" method="post">

    {{ csrf_field() }}

    <input type="hidden" name="phpmyadmin_tablename" value="{{ $name }}">
    <input type="hidden" name="phpmyadmin_tableid" value="{{ $id }}">

    @foreach ($columns as $column)
        @if ($column == "id")
            @php continue @endphp
        @endif
        <p>
            <input name="{{ $column }}" value="{{ $data->{$column} }}">
        </p>
    @endforeach
    
    <button type="submit">
        Edit row
    </button>
</form>

This will create a form, a required CSRF field. 2 hidden fields for table name and row ID which is being updating. Then looping through all columns, skipping the ID column and displaying them in input fields. Lastly, a submit button.

Now we need to create it’s post route in our web.php file:

Route::post("/edit_row", [PHPMyAdminController::class, "do_edit_row"]);

Now create it’s function “do_edit_row” in your controller class:

public function do_edit_row(Request $request)
{
    $data = DB::table($request->get("phpmyadmin_tablename"))->where("id", "=", $request->get("phpmyadmin_tableid"))->first();
    if ($data == null)
    {
        return redirect()->back()->with("error", "Record does not exists.");
    }
    $columns = Schema::getColumnListing($request->get("phpmyadmin_tablename"));
    $data = array();
    foreach ($columns as $column)
    {
        if ($column == "id")
        {
            continue;
        }
        $data[$column] = $request->get($column);
    }

    DB::table($request->get("phpmyadmin_tablename"))->where("id", "=", $request->get("phpmyadmin_tableid"))
        ->update($data);
    return redirect()->back()->with("success", "Record has been updated.");
}

First we are checking if the row exists in selected table. Then we are getting all columns of that table skipping the ID column. Finally, we are updating the database and returning the user back with a success message.

Create new table or add column in existing table using Laravel migration

Migrations in Laravel are used to change the structure in database, to create a new table or to add a column in existing table. To create a migration to create a new table, run the following command in your terminal:

php artisan make:migration create_my_test_table

This will create a file named create_my_test_table.php in database/migrations folder. Set the content of this file to the following:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateMyTestTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('my_test', function (Blueprint $table) {
            $table->id();
            $table->string("name");
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('my_test');
    }
}

To run the migration, you need to run the following command:

php artisan migrate

This will create a table named “my_test” in your database with 4 columns:

  1. ID auto increment unique primary key
  2. name TEXT
  3. created_at DATETIME
  4. updated_at DATETIME

Now if you want to create a new column in existing table, run the following command to create it’s migration:

php artisan make:migration add_email_to_my_test

This will create a file named “add_email_to_my_test.php” in database/migrations folder. Set the content of this file to the following:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddEmailToMyTest extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('my_test', function (Blueprint $table) {
            $table->string("email");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('my_test', function (Blueprint $table) {
            //
        });
    }
}

Now again you have to run the migration using following command:

php artisan migrate

This will create a new column named “email” with datatype TEXT in my_test table.

Add data using Laravel seeders

To create a seeder, first you need to run the following command in your terminal:

php artisan make:seeder MyTestSeeder

This will create a file named “MyTestSeeder.php” in database/seeders folder. Set the content of this file to the following:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

use DB;

class MyTestSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table("my_test")->insert([
            "name" => "Adnan",
            "email" => "adnan@gmail.com",
            "created_at" => now(),
            "updated_at" => now()
        ]);
    }
}

Now to run the seed, you have to run the following command:

php artisan db:seed --class=MyTestSeeder

This will run this specific seed and will insert a new row with values mentioned in the MyTestSeeder class.

[wpdm_package id=’834′]