article

Showing posts with label WordPress. Show all posts
Showing posts with label WordPress. Show all posts

Monday, March 24, 2025

Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication

Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication

Download Laravel App

https://laravel.com/docs/12.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel12dev
DB_USERNAME=root
DB_PASSWORD=root

Database Migration
php artisan migrate

myapp>php artisan migrate
Migration table created successfully.
check database table
Create Controller
myapp>php artisan make:controller PostController

app\Http\Controllers\PostController.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//app\Http\Controllers\PostController.php
<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Pagination\LengthAwarePaginator;
 
class PostController extends Controller
{
    public function index()
    {
        $response = Http::get(getenv("WP_JSON_Wordpress_URL"));
 
        if ($response->successful()) {
 
            $posts = $response->json();
 
            $postArr = collect($posts)->map(function ($post) {
                return [
                    'id' => $post['id'],
                    'title' => $post['title']['rendered'],
                    'content' => $post['content']['rendered'],
                ];
            })->toArray();
 
            return view('Home', compact('postArr'));
        } else {
            // Handle error if the request was not successful
            $error = $response->status(); // Get the HTTP status code
        }
    }
 
    public function create()
    {
        return view('Create');
    }
 
    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required',
            'content' => 'required',
            'status' => 'required',
        ]);
 
        $response = Http::withHeaders([
            'Authorization' => 'Bearer ' . getenv("WP_JWT_AUTH_TOKEN"),
            'Content-Type' => 'application/json',
        ])
            ->post(getenv("WP_JSON_Wordpress_URL"), [
                'title' => $request->title,
                'content' => $request->content,
                'status' => $request->status,
            ]);
 
        // response
        if ($response->successful()) {
 
            $post = $response->json();
 
            return redirect()->route('posts.index')->with('success', 'Post has been created successfully.');
        } else {
            // errors
            $error = $response->json();
 
            return redirect()->route('posts.create')->with('error', 'Failed to create post');
        }
    }
 
    public function show(Request $request)
    {
        $postId = $request->post;
 
        $url = getenv("WP_JSON_Wordpress_URL") . "/" . $postId;
 
        $response = Http::get($url);
 
        if ($response->successful()) {
            $post = $response->json();
            return view('Read', compact('post'));
        } else {
            return $response->status();
        }
    }
 
    public function edit(Request $request)
    {
        $postId = $request->post;
 
        $url = getenv("WP_JSON_Wordpress_URL") . "/" . $postId;
 
        $response = Http::get($url);
 
        if ($response->successful()) {
            $post = $response->json();
            return view('Edit', compact('post'));
        } else {
            return $response->status();
        }
    }
 
    public function update(Request $request)
    {
        $request->validate([
            'title' => 'required',
            'content' => 'required',
            'status' => 'required',
        ]);
 
        // ID of the post to update
        $postId = $request->post;
 
        $updateData = [
            'title' => $request->title,
            'content' => $request->content,
            'status' => $request->status
        ];
 
        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . getenv("WP_JWT_AUTH_TOKEN"),
        ])->put(getenv("WP_JSON_Wordpress_URL") . "/" . $postId, $updateData);
 
        if ($response->successful()) {
            $updatedPost = $response->json();
            return redirect()->route('posts.index')->with('success', 'Post Has Been updated successfully');
        } else {
            // Handle error
            $error = $response->status();
            return redirect()->route('posts.index')->with('error', 'Failed to update post');
        }
    }
 
    public function destroy(Request $request)
    {
        $postId = $request->post;
 
        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . getenv("WP_JWT_AUTH_TOKEN"),
        ])->delete(getenv("WP_JSON_Wordpress_URL") . "/" . $postId);
 
        if ($response->successful()) {
            $deletedPost = $response->json();
            return redirect()->route('posts.index')->with('success', 'Post has been deleted successfully');
        } else {
            $error = $response->status(); // Get the HTTP status code
            return redirect()->route('posts.index')->with('success', 'Failed to delete post');
        }
    }
 
    public function pagination(Request $request)
    {
 
        //First resolve the current page
        $page = LengthAwarePaginator::resolveCurrentPage();
        //Define how many items you want to display per page
        $perPage = 6;
 
        //Call the external API and retrieve the data passing the $page and the $perPage
        $response = Http::get(getenv("WP_JSON_Wordpress_URL"), [
            'page' => $page,
            'per_page' => $perPage,
        ]);
        $data = $response->json();
 
        $totalData = $response->header('X-WP-Total');
 
        //Create a new LengthAwarePaginator instance
        $paginator = new LengthAwarePaginator($data, $totalData,   $perPage, $page, [
           'path' => LengthAwarePaginator::resolveCurrentPath(),
        ]);
 
        //Pass the paginator instance to your Blade view
        //return $totalData;
        return view('Pagination', ['data' => $paginator]);
    }
}
View Blade File resources/views/Home.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//resources/views/Home.blade.php
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8"> <!--getbootstrap.com/docs/5.0/getting-started/introduction/ -->
    <title>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
 
<body>
    <div class="container mt-2">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h3>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</h3>
                </div>
                <h4 class="text-center">Post List</h4>
                <div class="pull-right mb-2" style="float: right;">
                    <a class="btn btn-success" href="{{ route('posts.create') }}">Create Post</a>
                </div>
            </div>
        </div>
        @if ($message = Session::get('success'))
        <div class="alert alert-success">
            <p>{{ $message }}</p>
        </div>
        @endif
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Post Title</th>
                    <th>Post Content</th>
                    <th width="280px">Action</th>
                </tr>
            </thead>
            <tbody>
                @foreach ($postArr as $post)
                <tr>
                    <td>{{ $post['id'] }}</td>
                    <td>{{ $post['title'] }}</td>
                    <td>{{ substr($post['content'], 0, 100) }}</td>
                    <td>
                        <form action="{{ route('posts.destroy',$post['id']) }}" method="Post">
                            <a class="btn btn-info" href="{{ route('posts.show',$post['id']) }}">Read</a>
                            <a class="btn btn-primary" href="{{ route('posts.edit',$post['id']) }}">Edit</a>
                            @csrf
                            @method('DELETE')
                            <button type="submit" onclick="return confirm('Are you sure want to delete?')" class="btn btn-danger">Delete</button>
                        </form>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>
    </div>
</body>
 
</html>
resources/views/Create.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//resources/views/Create.blade.php
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
 
<body>
    <div class="container mt-2">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h3>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</h3>
                </div>
                <h4 class="text-center">Add Post</h4>
                <div class="pull-right" style="float: right;">
                    <a class="btn btn-primary" href="{{ route('posts.index') }}"> Back</a>
                </div>
            </div>
        </div>
        @if(session('status'))
        <div class="alert alert-success mb-1 mt-1">
            {{ session('status') }}
        </div>
        @endif
        <form action="{{ route('posts.store') }}" method="POST">
            @csrf
            <div class="row">
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Title:</strong>
                        <input type="text" name="title" class="form-control" placeholder="Post Title">
                        @error('title')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Content:</strong>
                        <textarea class="form-control" name="content" rows="5" placeholder="Post Content"></textarea>
                        @error('content')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Status</strong>
                        <select name="status" class="form-control">
                            <option value="">-- Status --</option>
                            <option value="draft">Draft</option>
                            <option value="publish">Publish</option>
                        </select>
                        @error('status')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
 
                <button type="submit" class="btn btn-primary ml-3">Submit</button>
            </div>
        </form>
    </div>
</body>
 
</html>
resources/views/Read.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//resources/views/Read.blade.php
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication </title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
  
<body>
    <div class="container mt-2">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h3>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</h3>
                </div>
                <h4 class="text-center">View Post</h4>
                <div class="pull-right" style="float: right;">
                    <a class="btn btn-primary" href="{{ route('posts.index') }}">
                        Back</a>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12 col-sm-12 col-md-12">
                <div class="form-group">
                    <strong>Title:</strong>
                    {{ $post['title']['rendered'] }}
                </div>
            </div>
            <div class="col-xs-12 col-sm-12 col-md-12">
                <div class="form-group">
                    <strong>Content:</strong>
                    {!!html_entity_decode($post['content']['rendered'])!!}
                </div>
            </div>
        </div>
    </div>
</body>
 
</html>
resources/views/Edit.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//resources/views/Edit.blade.php
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication </title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
 
<body>
    <div class="container mt-2">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h3>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</h3>
                </div>
                <h4 class="text-center">Edit Post</h4>
                <div class="pull-right" style="float: right;">
                    <a class="btn btn-primary" href="{{ route('posts.index') }}">
                        Back</a>
                </div>
            </div>
        </div>
        @if(session('status'))
        <div class="alert alert-success mb-1 mt-1">
            {{ session('status') }}
        </div>
        @endif
        <form action="{{ route('posts.update',$post['id']) }}" method="POST">
            @csrf
            @method('PUT')
            <div class="row">
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Title:</strong>
                        <input type="text" name="title" value="{{ $post['title']['rendered'] }}" class="form-control"
                            placeholder="Post Title">
                        @error('name')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Content:</strong>
                        <textarea class="form-control" name="content" rows="5">{{ $post['content']['rendered'] }}</textarea>
                        @error('address')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="form-group">
                        <strong>Status:</strong>
                        <select name="status" id="status" class="form-control">
                            <option value="">-- Status --</option>
                            <option {{ $post["status"] == "draft" ? "selected" : "" }} value="draft">Draft</option>
                            <option {{ $post["status"] == "publish" ? "selected" : "" }} value="publish">Publish</option>
                        </select>
                        @error('address')
                        <div class="alert alert-danger mt-1 mb-1">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
 
                <button type="submit" class="btn btn-primary ml-3">Submit</button>
            </div>
        </form>
    </div>
</body>
 
</html>
resources/views/Edit.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//resources/views/Edit.blade.php
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <style>
        .custom-pagination {
            color: #0fb6cc;
            margin: 0 2px;
        }
 
        .custom-pagination .pagination {
            display: flex;
            justify-content: center;
            margin-top: 20px;
        }
    </style>
</head>
 
<body>
    <div class="container mt-2">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h3>Laravel 12 CRUD (Create, Read, Update and Delete) with Pagination WordPress REST API with JWT Authentication</h3>
                </div>
                <h4 class="text-center">Post List</h4>
                <div class="pull-right mb-2" style="float: right;">
                    <a class="btn btn-success" href="{{ route('posts.create') }}">Create Post</a>
                </div>
            </div>
        </div>
 
        @if ($message = Session::get('success'))
        <div class="alert alert-success">
            <p>{{ $message }}</p>
        </div>
        @endif
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Post Title</th>
                    <th>Post Content</th>
                    <th width="280px">Action</th>
                </tr>
            </thead>
            <tbody>
                @foreach ($data as $post)
                <tr>
                    <td>{{ $post['id'] }}</td>
                    <td>{{ $post['title']['rendered'] }}</td>
                    <td>{{ substr($post['content']['rendered'], 0, 100) }}</td>
                    <td>
                        <form action="{{ route('posts.destroy',$post['id']) }}" method="Post">
                            <a class="btn btn-info" href="{{ route('posts.show',$post['id']) }}">Read</a>
                            <a class="btn btn-primary" href="{{ route('posts.edit',$post['id']) }}">Edit</a>
                            @csrf
                            @method('DELETE')
                            <button type="submit" onclick="return confirm('Are you sure want to delete?')" class="btn btn-danger">Delete</button>
                        </form>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>
 
        <!--
        {{ $data->links() }} -->
        <div class="d-flex custom-pagination">
            {{ $data->links('pagination::bootstrap-5') }}
        </div>
 
 
    </div>
</body>
 
</html>
Routes
routes/web.php
1
2
3
4
5
6
7
8
9
10
//routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
 
Route::get('/', function () {
    return view('welcome');
});
 
Route::resource('posts', PostController::class);
Route::get('/pagination', [PostController::class, 'pagination'])->name('pagination');
Install wordpress plugin JWT Authentication for WP-API and activate. wordpress org/plugins/jwt-authentication-for-wp-rest-api/

Configurate the Secret Key and enable the CORs Support
To add the secret key edit your wp-config.php file and add a new constant called JWT_AUTH_SECRET_KEY JWT_AUTH_CORS_ENABLE

define('JWT_AUTH_SECRET_KEY', 'cairocoders-ednalan0711');
define('JWT_AUTH_CORS_ENABLE', true);

Open laravel .env file from project folder, add token and json url
WP_JWT_AUTH_TOKEN="0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJod..."
WP_JSON_POST_URL="https://yourwordpresssite.com/wp-json/wp/v2/posts"

Sunday, March 16, 2025

Reactjs 18 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS

Reactjs 18 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS

react.dev
Create react project
Run the following command in your terminal: vite dev/guide/
npm create vite@latest
tanstack/react-query
Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue

npm i @tanstack/react-query

react-query-devtools
npm i @tanstack/react-query-devtools

Tailwind CSS with Vite
tailwindcss com/docs/guides/vite

Install npm i uuid

my-app\src\App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//src\App.jsx
import { Route, Routes } from "react-router-dom" //npm i react-router-dom
import Home from "./pages/Home"
import Post from "./pages/Post"
 
function App() {
  return (
    <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/post/:slug" element={<Post />} />
    </Routes>
  )
}
    
export default App
my-app\src\main.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//src\main.jsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import { BrowserRouter } from "react-router-dom";
import App from './App.jsx'
  
// create a client
const queryClient = new QueryClient();
  
createRoot(document.getElementById("root")).render(
  <StrictMode>
    <BrowserRouter>
      <QueryClientProvider client={queryClient}>
        <App />
      </QueryClientProvider>
    </BrowserRouter>
  </StrictMode>,
);
Home Page : my-app\src\pages\Home.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//src\pages\Home.jsx
import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import { getPostList } from "../api/post";
import Pagination from "../components/Pagination";
import { useSearchParams } from "react-router-dom"
import PostList from "./PostList";
 
export default function Home() {
    const [searchParams] = useSearchParams();
    const page = Number(searchParams.get("page"));
    //console.log(page)
   
    const currentPage = Number(page) || 1;
    //console.log(currentPage)
 
    const { isLoading, data, isError,  error } = useQuery({
        queryKey: ["customers", currentPage],
        queryFn: () => getPostList(currentPage)
    });
    //console.log(data);
 
    if (isLoading) return "Loading...";
    if (isError) return `Error: ${error.message}`;
 
    const totalPages = Math.ceil(Number(data.totalpage) / Number(data.perpage));
    console.log(totalPages);
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
        <div className="flex items-center justify-between gap-1 mb-5 pl-10 pr-10">
            <h1 className="text-4xl font-bold">Reactjs 18 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS</h1>
        </div>
        <div className="overflow-x-auto pt-10">
            <PostList postlists={data.postlist} />
            <div className="flex items-center justify-between my-5">
                <Pagination totalPages={totalPages}/>
            </div>
        </div>
    </div>
  );
}
Post List page : my-app\src\pages\PostList.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//my-app\src\pages\PostList.jsx
const PostList = ({ postlists }) => {
    return (
    <>
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {postlists.map((post) => (
          <div key={post.id} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url}
                alt={post.title.rendered}
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"/>
            </div>
            <div className="p-5">
              <h1>
                <a
                  href={`/post/${post.slug}`}>
                  {post.title.rendered}
                </a>        
              </h1>
            </div>
          </div>
        ))}
      </div>
    </>
  );
};
   
export default PostList;
View page : my-app\src\pages\Post.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//my-app\src\pages\Post.jsx
import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import { useParams } from "react-router-dom";
import { fetchPost } from "../api/post";
   
const Read = () => {
    const { slug } = useParams();
    console.log(slug);
  
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: ["posts", slug],
        queryFn: () => fetchPost(slug),
    });
    console.log(post);
    if (isLoading) return "loading...";
    if (isError) return `Error: ${error.message}`;
   
  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8 text-black">
            {
                post.length ? (
                <div>
                  <h1 className="text-2xl font-bold mb-5">{post[0].title.rendered}</h1>
                    <div className="mb-4">           
                      <div
                        dangerouslySetInnerHTML={{ __html: post[0]['content']['rendered'] }}
                      />  
                    </div>
                </div>
                ) : ('Loading....')
               
            }
      </div>
    </div>
  )
}
   
export default Read
Api : my-app\src\api\post.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//src\api\post.jsx
const ITEM_PER_PAGE = 6;
export async function getPostList(page) {
  const response = await fetch(`http://localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${ITEM_PER_PAGE}&page=${page}&_embed`);
  const totalData = response.headers.get('X-WP-Total');
  const data = await response.json();
  console.log(data);
  return {
    postlist: data,
    totalpage: totalData,
    perpage: ITEM_PER_PAGE
  }
}
  
export async function fetchPost(slug) {
  return response.json()
}
Pagination : my-app\src\components\Pagination.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//src\components\Pagination.jsx
import { HiChevronLeft, HiChevronRight } from "react-icons/hi"; //npm install react-icons --save npmjs com/package/react-icons
import clsx from "clsx"; //npm i clsx npmjs com/package/clsx
import { useSearchParams, useLocation } from "react-router-dom"
 
export const generatePagination = (currentPage, totalPages) => {
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }
    
  if (currentPage <= 3) {
    return [1, 2, 3, "...", totalPages - 1, totalPages];
  }
   
  if (currentPage >= totalPages - 2) {
    return [1, 2, 3, "...", totalPages - 2, totalPages - 1, totalPages];
  }
   
  return [
    1,
    "...",
    currentPage - 1,
    currentPage,
    currentPage + 1,
    "...",
    totalPages,
  ];
};
 
const Paginationnumber = ({ totalPages }) => {
  let location = useLocation();
  console.log(location);
  const { hash, pathname, search } = location;
  //console.log(pathname);
 
  const [searchParams] = useSearchParams();
  const currentPage = Number(searchParams.get("page")) || 1;
  //console.log(currentPage); 2
 
  const allPages = generatePagination(currentPage, totalPages);
 
  const createPageURL = (pageNumber) => {
      const params = searchParams;
      params.set("page", pageNumber.toString());
      return `${pathname}?${params.toString()}`;
  };
 
  const PaginationNumber = ({
      page,
      href,
      position,
      isActive,
  }) => {
      const className = clsx(
          "flex h-10 w-10 items-center justify-center text-sm border",
          {
              "rounded-l-sm": position === "first" || position === "single",
              "rounded-r-sm": position === "last" || position === "single",
              "z-10 bg-blue-700 border-blue-500 text-white bg-blue-700": isActive,
              "hover:bg-blue-700": !isActive && position !== "middle",
              "text-gray-300 pointer-events-none": position === "middle",
          }
      );
 
      return isActive && position === "middle" ? (
          <div className={className}>{page}</div>
      ) : (
          <a href={href} className={className}>
              {page}
          </a>
      );
  };
 
  const PaginationArrow = ({ href, direction, isDisabled }) => {
      const className = clsx(
          "flex h-10 w-10 items-center justify-center text-sm border",
          {
              "pointer-events-none text-blue-300": isDisabled,
              "hover:bg-blue-700": !isDisabled,
              "mr-2": direction === "left",
              "ml-2": direction === "right",
          }
      );
 
      const icon =
        direction === "left" ? (
          <HiChevronLeft size={20} />
        ) : (
          <HiChevronRight size={20} />
        );
 
    return isDisabled ? (
      <div className={className}>{icon}</div>
    ) : (
      <a href={href} className={className}>
        {icon}
      </a>
    );
  };
 
  return (
    <div className="inline-flex">
      <PaginationArrow
        direction="left"
        href={createPageURL(currentPage - 1)}
        isDisabled={currentPage <= 1}
      />
      <div className="flex -space-x-px">
        {allPages.map((page, index) => {
          let position;
 
          if (index === 0) position = "first";
          if (index === allPages.length - 1) position = "last";
          if (allPages.length === 1) position = "single";
          if (page === "...") position = "middle";
 
          return (
            <PaginationNumber
              key={index}
              href={createPageURL(page)}
              page={page}
              position={position}
              isActive={currentPage === page}
            />
          );
        })}
      </div>
      <PaginationArrow
        direction="right"
        href={createPageURL(currentPage + 1)}
        isDisabled={currentPage >= totalPages}
      />
    </div>
  );
};
 
export default Paginationnumber;
Run
C:\react-js\my-app> npm run dev

themes\cairocoders\functions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//themes\cairocoders\functions.php
<?php
/**
 * Theme functions and definitions
 *
 * @package cairocoders
 */
 
add_action('rest_api_init', 'register_rest_images' );
 
function register_rest_images(){
    register_rest_field( array('post'),
        'fimg_url',
        array(
            'get_callback'    => 'get_rest_featured_image',
            'update_callback' => null,
            'schema'          => null,
        )
    );
}
 
function get_rest_featured_image( $object, $field_name, $request ) {
    if( $object['featured_media'] ){
        $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' );
        return $img[0];
    }
    return false;
}

Sunday, February 9, 2025

Nextjs 15 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS

Nextjs 15 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS

Install nextjs npx create-next-app@latest nextjs_org/docs/getting-started/installation

Install react-query

npm i @tanstack/react-query
tanstack com/query/latest/docs/framework/react/installation
app\page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//app\page.tsx
"use client";
 
import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import PostList from "./components/PostList";
import { getPostList } from "./api/post";
import LoadingSpinner from "./components/LoadingSpinner";
import Paginationnumber from "./components/Paginationnumber";
import { useSearchParams } from 'next/navigation'
 
export default function Home() {
 
    const searchParams = useSearchParams()
    const page = searchParams.get('page')
 
    const currentPage = Number(page) || 1;
    //const currentPage = 1;
 
    const { isLoading, data, isError, isFetching,  error } = useQuery({
        queryKey: ["posts", currentPage],
        queryFn: () => getPostList(currentPage)
    });
    //console.log(data);
    if (isLoading) return "Loading...";
    if (isError) return `Error: ${error.message}`;
 
    const ITEMS_PER_PAGE = 6;
    const totalPages = Math.ceil(Number(data.totalData) / ITEMS_PER_PAGE);
    //console.log(totalPages)
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
        <div className="flex items-center justify-between gap-1 mb-5 pl-10 pr-5">
            <h1 className="text-4xl font-bold">Nextjs 15 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS</h1>
        </div>
        <div className="overflow-x-auto py-10">
            <PostList postlist={data.postlist} />
            <div className="flex items-center justify-between my-5">
                <Paginationnumber totalPages={totalPages} />
                {isFetching ? <LoadingSpinner /> : null}
            </div>
        </div>
    </div>
  );
}
app\layout.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//app\layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import QueryProvider from "./components/QueryProvider";
 
const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});
 
const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});
 
export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};
 
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <QueryProvider>{children}</QueryProvider>
      </body>
    </html>
  );
}
app\components\QueryProvider.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//app\components\QueryProvider.tsx
"use client";
 
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState } from "react";
 
interface Props {
  children: React.ReactNode;
}
 
export default function QueryProvider({ children }: Props) {
  const [queryClient] = useState(() => new QueryClient());
  return (
    <QueryClientProvider client={queryClient}> {children} </QueryClientProvider>
  );
}
app\components\PostList.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//app\components\PostList.tsx
const PostList = ({ postlist }) => {
 
  return (
    <>
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {postlist.map((post) => (
          <div key={post.id} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url}
                alt={post.title.rendered}
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"/>
            </div>
            <div className="p-5">
              <h1>
                <a
                  href={`/post/${post.slug}`}>
                  {post.title.rendered}
                </a>        
              </h1>
            </div>
          </div>
        ))}
      </div>
    </>
  )
}
  
export default PostList
app\components\LoadingSpinner.tsx
1
2
3
4
5
6
//app\components\LoadingSpinner.tsx
export default function LoadingSpinner() {
    return (
        <h1>Loading...</h1>
    )
}
app\components\Paginationnumber.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//app\components\Paginationnumber.tsx
"use client";
  
import Link from "next/link";
import { HiChevronLeft, HiChevronRight } from "react-icons/hi"; //npm install react-icons --save npmjs com/package/react-icons
import { usePathname, useSearchParams } from "next/navigation";
import clsx from "clsx"; //npm i clsx npmjs com/package/clsx
  
export const generatePagination = (currentPage: number, totalPages: number) => {
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }
  
  if (currentPage <= 3) {
    return [1, 2, 3, "...", totalPages - 1, totalPages];
  }
  
  if (currentPage >= totalPages - 2) {
    return [1, 2, 3, "...", totalPages - 2, totalPages - 1, totalPages];
  }
  
  return [
    1,
    "...",
    currentPage - 1,
    currentPage,
    currentPage + 1,
    "...",
    totalPages,
  ];
};
 
const Paginationnumber = ({ totalPages }: { totalPages: number }) => {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const currentPage = Number(searchParams.get("page")) || 1;
  
  const createPageURL = (pageNumber: string | number) => {
    const params = new URLSearchParams(searchParams);
    params.set("page", pageNumber.toString());
    return `${pathname}?${params.toString()}`;
  };
  
  const allPages = generatePagination(currentPage, totalPages);
  
  const PaginationNumber = ({
    page,
    href,
    position,
    isActive,
  }: {
    page: number | string;
    href: string;
    position?: "first" | "last" | "middle" | "single";
    isActive: boolean;
  }) => {
    const className = clsx(
      "flex h-10 w-10 items-center justify-center text-sm border",
      {
        "rounded-l-sm": position === "first" || position === "single",
        "rounded-r-sm": position === "last" || position === "single",
        "z-10 bg-blue-100 border-blue-500 text-white bg-blue-700": isActive,
        "hover:bg-blue-700": !isActive && position !== "middle",
        "text-gray-300 pointer-events-none": position === "middle",
      }
    );
  
    return isActive && position === "middle" ? (
      <div className={className}>{page}</div>
    ) : (
      <Link href={href} className={className}>
        {page}
      </Link>
    );
  };
    
  const PaginationArrow = ({
    href,
    direction,
    isDisabled,
  }: {
    href: string;
    direction: "left" | "right";
    isDisabled?: boolean;
  }) => {
    const className = clsx(
      "flex h-10 w-10 items-center justify-center text-sm border",
      {
        "pointer-events-none text-gray-300": isDisabled,
        "hover:bg-gray-100": !isDisabled,
        "mr-2": direction === "left",
        "ml-2": direction === "right",
      }
    );
  
    const icon =
      direction === "left" ? (
        <HiChevronLeft size={20} />
      ) : (
        <HiChevronRight size={20} />
      );
  
    return isDisabled ? (
      <div className={className}>{icon}</div>
    ) : (
      <Link href={href} className={className}>
        {icon}
      </Link>
    );
  };
  
  return (
    <div className="inline-flex">
      <PaginationArrow
        direction="left"
        href={createPageURL(currentPage - 1)}
        isDisabled={currentPage <= 1}
      />
      <div className="flex -space-x-px">
        {allPages.map((page, index) => {
          let position: "first" | "last" | "single" | "middle" | undefined;
  
          if (index === 0) position = "first";
          if (index === allPages.length - 1) position = "last";
          if (allPages.length === 1) position = "single";
          if (page === "...") position = "middle";
  
          return (
            <PaginationNumber
              key={index}
              href={createPageURL(page)}
              page={page}
              position={position}
              isActive={currentPage === page}
            />
          );
        })}
      </div>
      <PaginationArrow
        direction="right"
        href={createPageURL(currentPage + 1)}
        isDisabled={currentPage >= totalPages}
      />
    </div>
  );
};
  
export default Paginationnumber;
app\post\[slug]\page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//app\post\[slug]\page.tsx
"use client";
  
import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query
import { useParams } from 'next/navigation'
import { fetchPost } from "../../api/post";
   
const Post = () => {
  
  const {slug}=useParams();
  
  const {
    isLoading,
    isError,
    data: post,
    error,
  } = useQuery({
    queryKey: ["post", slug],
    queryFn: () => fetchPost(slug),
  });
   
  if (isLoading) return "loading...";
  if (isError) return `Error: ${error.message}`;
   
   
  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8 text-black">
            {
                post.length ? (
                <div>
                  <h1 className="text-2xl font-bold mb-5">{post[0].title.rendered}</h1>
                    <div className="mb-4">           
                      <div
                        dangerouslySetInnerHTML={{ __html: post[0]['content']['rendered'] }}
                      />  
                    </div>
                </div>
                ) : ('Loading....')
              
            }
      </div>
    </div>
  )
}
   
export default Post
app\api\post.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//app\api\post.tsx
const ITEM_PER_PAGE = 6;
export async function getPostList(page) {
  const response = await fetch(`http://localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${ITEM_PER_PAGE}&page=${page}&_embed`);
  const totalData = response.headers.get('X-WP-Total');
  const data = await response.json();
  //console.log(data);
  return {
    postlist: data,
    totalData
  }
}
 
export async function fetchPost(slug) {
  return response.json()
}
themes\cairocoders\functions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//themes\cairocoders\functions.php
<?php
/**
 * Theme functions and definitions
 *
 * @package cairocoders
 */
 
add_action('rest_api_init', 'register_rest_images' );
 
function register_rest_images(){
    register_rest_field( array('post'),
        'fimg_url',
        array(
            'get_callback'    => 'get_rest_featured_image',
            'update_callback' => null,
            'schema'          => null,
        )
    );
}
 
function get_rest_featured_image( $object, $field_name, $request ) {
    if( $object['featured_media'] ){
        $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' );
        return $img[0];
    }
    return false;
}
run C:\nextjs>npm run dev

Monday, November 25, 2024

React.js WordPress Rest API Pagination with Featured Image and View Single Page | Tailwind CSS

React.js WordPress Rest API Pagination with Featured Image and View Single Page | Tailwind CSS
React JS
https://react.dev/learn/start-a-new-react-project

npx create-next-app@latest
npx create-react-app@latest {project name}
Create Project
C:\react-js>npx create-react-app@latest my-app
Run
C:\react-js\my-app> npm start

Install tailwindcss https://tailwindcss.com/docs/guides/create-react-app
npm install -D tailwindcss
npx tailwindcss
init Install

react-router-dom
https://www.npmjs.com/package/react-router-dom Install axios
src\App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//src\App.js
import { BrowserRouter, Routes, Route } from "react-router-dom"; //npm i react-router-dom https://www.npmjs.com/package/react-router-dom
import Home from "./elements/Home";
import ViewPage from "./elements/ViewPage";
 
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/posts/:slug" element={<ViewPage />} />
      </Routes>
    </BrowserRouter>
  );
}
 
export default App;
src\elements\Home.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//src\elements\Home.js
import React  from 'react';
import Posts from './Posts'
import { Suspense } from "react";
import { IoSearch } from "react-icons/io5"; //https://www.npmjs.com/package/react-icons npm install react-icons --save
 
function Home() {
 
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
        <div className="flex items-center justify-between gap-1 mb-5">
            <h1 className="text-4xl font-bold">React.js WordPress Rest API Pagination with Featured Image and View Single Page | Tailwind CSS</h1>
        </div>
        <div className="overflow-x-auto py-10">
            <div className="mb-2 w-full text-right">
                <div className="relative flex flex-1">
                <input
                    type="text"
                    className="w-full border border-gray-200 py-2 pl-10 text-sm outline-2 rounded-sm"
                    placeholder="Search..."
                />
                <IoSearch className="absolute left-3 top-2 h-5 w-5 text-gray-500" />
                </div>
            </div>
            <Suspense fallback="Loading...">
                <Posts/>
            </Suspense>
        </div>
    </div>
  )
}
 
export default Home
src\elements\Posts.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//src\elements\Posts.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
 
export default function Posts() {
    const [posts, setPosts] = useState([]);
 
    const [currentPage, setCurrentPage] = useState(1);
    const [totalPages, setTotalPages] = useState(1);
 
    const perPage = 3;
 
    useEffect(() => {
        let url = `http://localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${perPage}&page=${currentPage}&_embed`;
        axios.get(url).then((res) => {
            const { data, headers } = res;
            setTotalPages(Number(headers['x-wp-totalpages']));
            setPosts(data);
            //console.log("Headers", headers['x-wp-totalpages']);
        });
    }, [currentPage])
    //console.log('posts', posts);
 
  return (
    <div>
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {posts.map((post, index) => (
          <div key={index} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url}
                alt={post.title.rendered}
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"/>
            </div>
            <div className="p-5">
              <h1>
                <a
                  href={`/posts/${post.slug}`}>
                  {post.title.rendered}
                </a>        
              </h1>
            </div>
          </div>
        ))}
      </div>
      { posts.length > 0 && (
        <div className='w-1/2 py-10 m-auto flex justify-between items-center align-middle flex-wrap gap-10'>
          <button className='btn-primary p-2 bg-blue-500 text-white text-lg rounded-lg hover:shadow-lg disabled:opacity-50'
            disabled={currentPage === 1}
            onClick={() => setCurrentPage(currentPage - 1)}
          >
          Previous
          </button>
 
          <span className='text-lg'>{currentPage} of {totalPages}</span>
 
          <button className='btn-primary p-2 bg-blue-500 text-white text-lg rounded-lg hover:shadow-lg disabled:opacity-50'
            disabled={currentPage === totalPages}
            onClick={() => setCurrentPage(currentPage + 1)}
          >
          Next
          </button>
        </div>
        )
      }
    </div>                         
 );
}
src\elements\ViewPage.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//src\elements\ViewPage.js
import axios from 'axios';
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom';
 
const Single = () => {
    const { slug } = useParams();
    const [post, setPost] = useState({});
 
    useEffect(()=>{
        // axios
        axios.get(url).then(res => {
            console.log('res', res);
            //console.log(res.data[0].id);
            setPost(res.data);
        }).catch(err => {
            console.log('error:', err.message);
        });
    }, []);
    return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
            {
                post.length ? (
                <div>
                  <h1 className="text-2xl font-bold mb-5">{post[0].title.rendered}</h1>
                    <div className="mb-4">           
                      <div
                        dangerouslySetInnerHTML={{ __html: post[0]['content']['rendered'] }}
                      />  
                    </div>
                </div>
                ) : ('Loading....')
             
            }
      </div>
    </div>
    )
}
export default Single;
themes\cairocoders\functions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//themes\cairocoders\functions.php
<?php
/**
 * Theme functions and definitions
 *
 * @package cairocoders
 */
 
add_action('rest_api_init', 'register_rest_images' );
 
function register_rest_images(){
    register_rest_field( array('post'),
        'fimg_url',
        array(
            'get_callback'    => 'get_rest_featured_image',
            'update_callback' => null,
            'schema'          => null,
        )
    );
}
 
function get_rest_featured_image( $object, $field_name, $request ) {
    if( $object['featured_media'] ){
        $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' );
        return $img[0];
    }
    return false;
}
Run C:\react-j\my-app>npm start
http://localhost:3000/

Thursday, November 21, 2024

React.js + WordPress Load Posts with Featured Image and View Single Page | Tailwind CSS

React.js + WordPress Load Posts with Featured Image and View Single Page | Tailwind CSS

React JS
https://react.dev/learn/start-a-new-react-project

npx create-next-app@latest
npx create-react-app@latest {project name}
Create Project
C:\react-js>npx create-react-app@latest my-app
Run
C:\react-js\my-app> npm start

Install tailwindcss https://tailwindcss.com/docs/guides/create-react-app
npm install -D tailwindcss
npx tailwindcss
init Install

react-router-dom
https://www.npmjs.com/package/react-router-dom Install axios
src\App.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//src\App.js
import { BrowserRouter, Routes, Route } from "react-router-dom"; //npm i react-router-dom https://www.npmjs.com/package/react-router-dom
import Home from "./elements/Home";
import ViewPage from "./elements/ViewPage";
 
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/posts/:slug" element={<ViewPage />} />
      </Routes>
    </BrowserRouter>
  );
}
 
export default App;
src\elements\Home.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//src\elements\Home.js
import React  from 'react';
import Posts from './Posts'
import { Suspense } from "react";
import { IoSearch } from "react-icons/io5"; //https://www.npmjs.com/package/react-icons npm install react-icons --save
 
function Home() {
 
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
        <div className="flex items-center justify-between gap-1 mb-5">
            <h1 className="text-4xl font-bold">React.js + WordPress Load Posts with Featured Image and View Single Page | Tailwind CSS</h1>
        </div>
        <div className="overflow-x-auto py-10">
            <div className="mb-2 w-full text-right">
                <div className="relative flex flex-1">
                <input
                    type="text"
                    className="w-full border border-gray-200 py-2 pl-10 text-sm outline-2 rounded-sm"
                    placeholder="Search..."
                />
                <IoSearch className="absolute left-3 top-2 h-5 w-5 text-gray-500" />
                </div>
            </div>
            <Suspense fallback="Loading...">
                <Posts/>
            </Suspense>
        </div>
    </div>
  )
}
 
export default Home
src\elements\Posts.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//src\elements\Posts.js
import React, { useEffect, useState } from 'react';
 
export default function Posts() {
    const [posts, setPosts] = useState([]);
    useEffect(() => {
        async function loadPosts() {
            const response = await fetch('http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed');
            if(!response.ok) {
                // oups! something went wrong
                return;
            }
     
            const posts = await response.json();
            setPosts(posts);
        }
     
        loadPosts();
   }, [])
  return (
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {posts.map((post, index) => (
          <div key={index} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url}
                alt={post.title.rendered}
                fill
                priority
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"/>
            </div>
            <div className="p-5">
              <h1>
                <a
                  href={`/posts/${post.slug}`}>
                  {post.title.rendered}
                </a>        
              </h1>
            </div>
          </div>
        ))}
      </div>
 );
}
src\elements\ViewPage.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//src\elements\ViewPage.js
import React from 'react';
import { useEffect, useState } from 'react';
import { useParams } from "react-router-dom";
 
export default function Posts() {
    const {slug}=useParams();
 
    const [posts, setData] = useState(null);
 
    useEffect(()=>{
        fetchPost();
    },[slug]);
      
    const fetchPost=async()=>{
        try{
          const response = await fetch("http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed&slug="+slug);
            if(!response.ok) {
                // oups! something went wrong
                return;
            }
 
            const posts = await response.json();
            //console.log(posts);
            setData(posts);
        }catch(err){
            console.log("Something Wrong");
        }
    }
 
    return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
        {posts && posts.map((post, index) => (
        <div key={index}>
          <h1 className="text-2xl font-bold mb-5">{post.title.rendered}</h1>
            <div className="mb-4">           
              <div
                dangerouslySetInnerHTML={{ __html: post['content']['rendered'] }}
              />  
            </div>
        </div>
        ))}
     </div>
    </div>
  );
}
themes\cairocoders\functions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//themes\cairocoders\functions.php
<?php
/**
 * Theme functions and definitions
 *
 * @package cairocoders
 */
 
add_action('rest_api_init', 'register_rest_images' );
 
function register_rest_images(){
    register_rest_field( array('post'),
        'fimg_url',
        array(
            'get_callback'    => 'get_rest_featured_image',
            'update_callback' => null,
            'schema'          => null,
        )
    );
}
 
function get_rest_featured_image( $object, $field_name, $request ) {
    if( $object['featured_media'] ){
        $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' );
        return $img[0];
    }
    return false;
}
Run C:\react-j\my-app>npm start
http://localhost:3000/

Wednesday, November 13, 2024

Nextjs + WordPress Load Posts with Featured Image and View Single Page

Nextjs + WordPress Load Posts with Featured Image and View Single Page

app\posts\page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//app\posts\page.tsx
import { Suspense } from "react";
import { Spinner } from "@/components/spinner";
import PostTable from "@/components/tableposts";
 
const Posts = async () => {
 
  return (
    <div className="max-w-screen-lg mx-auto py-14">
      <h1 className="text-4xl font-bold">Nextjs + WordPress Load Posts with Featured Image and View Single Page</h1>
      <div className="flex items-end justify-between m-12">
        <h1 className="text-4xl font-bold">Latest Post</h1>
      </div>
 
      <Suspense fallback={<Spinner />}>
        <PostTable/>
      </Suspense>
 
    </div>
  );
};
 
export default Posts;
app\components\tableposts.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//app\components\tableposts.tsx
import Image from "next/image";
import Link from "next/link";
import { formatDate } from "@/lib/utils";
 
const PostTable = async () => {
  const data = await res.json();
 
  return (
      <div className="grid md:grid-cols-3 gap-5 mt-10">
        {data.map((post: any) => (
          <div key={post.id} className="max-w-sm border border-gray-200 rounded-md shadow">
            <div className="relative aspect-video">
              <Image
                src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url}
                alt={post.title.rendered}
                fill
                priority
                sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
                className="rounded-t-md object-cover"
              />
            </div>
            <div className="p-5">
              <h1>
                <Link href={`/posts/${post.slug}`}>{post.title.rendered}</Link>
              </h1>
            </div>
            <div className="flex items-center justify-between">
              <div className="py-3 text-sm text-black rounded-bl-md w-full text-center">{formatDate(post.date.toString())}</div>
            </div>
          </div>
        ))}
      </div>
  );
};
 
export default PostTable;
app\components\Spinner.tsx
1
2
3
4
5
6
//app\components\Spinner.tsx
export const Spinner = () => {
  return (
    <span className="loading loading-spinner loading-lg">Loading</span>
  );
};
app\posts\[slug]\page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//app\posts\[slug]\page.tsx
import { notFound } from "next/navigation";
  
const PostPage = async ({ params }: { params: { slug: string } }) => {
  const slug = params.slug;
  //console.log(data);
 
  const res = await fetch(`http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed&slug=${slug}`);
  const data = await res.json();
 
  if (!data) return notFound();
  
  return (
    <div className="min-h-screen flex items-center justify-center bg-slate-100">
      <div className="bg-white rounded-sm shadow p-8">
        {data.map((post) => (
          <div key={post.id}>
            <h1 className="text-2xl font-bold mb-5">{post.title.rendered}</h1>
            <div className="mb-4">           
              <div
                dangerouslySetInnerHTML={{ __html: post['content']['rendered'] }}
              />  
            </div>
          </div>
        ))}
 
      </div>
    </div>
  );
};
  
export default PostPage;
next.config.mjs
1
2
3
4
5
6
7
8
9
10
11
//next.config.mjs
/** @type {import('next').NextConfig} */
//const nextConfig = {};
const nextConfig = {
  reactStrictMode: true,
  images : {
    domains : ['localhost'] // Domain name
  }
}
 
export default nextConfig;
themes\cairocoders\functions.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//themes\cairocoders\functions.php
<?php
/**
 * Theme functions and definitions
 *
 * @package cairocoders
 */
 
add_action('rest_api_init', 'register_rest_images' );
 
function register_rest_images(){
    register_rest_field( array('post'),
        'fimg_url',
        array(
            'get_callback'    => 'get_rest_featured_image',
            'update_callback' => null,
            'schema'          => null,
        )
    );
}
 
function get_rest_featured_image( $object, $field_name, $request ) {
    if( $object['featured_media'] ){
        $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' );
        return $img[0];
    }
    return false;
}
run C:\nextjs>npm run dev

Tuesday, February 19, 2019

How to create wordpres plugins custom admin menu

How to create wordpres plugins custom admin menu

1. first create folder wp-content\plugins\admin_menu
2. create php file admin.php

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
/**
* Plugin Name: Admin Menu
* Description: A custom admin menu.
* Version: 1.0
* Author: Cairocoders
**/
function theme_options_panel(){
  add_menu_page('Theme page title', 'Theme menu', 'manage_options', 'theme-options', 'wps_theme_func');
  add_submenu_page('theme-options', 'Settings page title', 'Settings menu label', 'manage_options', 'theme-op-settings', 'wps_theme_func_settings');
  add_submenu_page('theme-options', 'FAQ page title', 'FAQ menu label', 'manage_options', 'theme-op-faq', 'wps_theme_func_faq');
}
add_action('admin_menu', 'theme_options_panel');
//function
function wps_theme_func(){
        echo '<div class="wrap"><div id="icon-options-general" class="icon32"><br></div>
        <h2>Theme</h2></div>';
}
function wps_theme_func_settings(){
        echo '<div class="wrap"><div id="icon-options-general" class="icon32"><br></div>
        <h2>Settings</h2></div>';
}
function wps_theme_func_faq(){
        echo '<div class="wrap"><div id="icon-options-general" class="icon32"><br></div>
        <h2>FAQ</h2></div>';
}

Tuesday, February 5, 2019

How to create WordPress plugins with Shortcode Ajax

How to create WordPress plugins with Shortcode Ajax

1. Create folder name wp-content\plugins\"myplugins_shortcode_ajax"
2. Add index.php file wp-content\plugins\myplugins_shortcode_ajax\index.php
3. Add code index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//index.php
<?php
/**
* Plugin Name: Shortcode ajax
* Description: How to add a form to a shortcode in WordPress (using PHP and Ajax)
* Version: 1.0
* Author: Cairocoders
**/
$ajaxpostExample = new ajaxpostExample();
class ajaxpostExample {
 public function __construct() {
        $this->attach_actions();
  $this->attach_shortcodes();
    }
 function attach_actions() {
        add_action('wp_ajax_nopriv_ajax_do_something', array($this, 'do_something_serverside'));
        add_action('wp_ajax_ajax_do_something', array($this, 'do_something_serverside')); /* notice green_do_something appended to action name of wp_ajax_ */
        add_action('wp_enqueue_scripts', array($this, 'theme_enqueue_styles'), 99999);
    }
 function theme_enqueue_styles() {
        //include the js
  wp_enqueue_script('ajaxsample_script', plugin_dir_url(__FILE__) . 'js/ajax_call_to_handle_form_submit.js', array('jquery'));
  wp_enqueue_style('handlecss',  plugin_dir_url(__FILE__) . 'css/ajaxshortcodestyle.css');
  // in JavaScript, object properties are accessed as ajax_object.ajax_url, ajax_object.we_value
  // Register the script
  //wp_localize_script( $handle, $name, $data );
  wp_localize_script('ajaxsample_script', 'ajax_object', array('ajax_url' => admin_url('admin-ajax.php'), 'we_value' => 1234));
 }
 function do_something_serverside() {
  global $wpdb;
  // output to ajax call  
  $txtname = $_POST['txtname'];
  $txtemail = $_POST['txtemail'];
  $txtsubject = $_POST['txtsubject'];
  $txtmessage = $_POST['txtmessage'];
  if ($txtname=='' OR $txtemail==''){
   echo "false";
  }else{
   echo "true";
  }
  //insert to a custom database table
  $wpdb->insert("test", array(
   "contactname" => $txtname
  ));
        die();
    }
 function attach_shortcodes() {
        add_shortcode('shortcode_ajax_form', array($this, 'shortcode_with_form_code')); //<p>[shortcode_ajax_form]</p>
    }
    function shortcode_with_form_code($atts, $content = null) {
        if ($this->formOk()) {
            return " <div>thanks your form has been submitted</div>";
        } else {
            return $this->show_form();
        }
    }
 function show_form() {
        $form = '<div class="well"><div id="display_rec"></div>
        <form id="green_form" action="">
          Name:<br>
          <input type="text" name="txtname" class="form-control" value="">
          <br>
          Email Address:<br>
          <input type="text" name="txtemail" class="form-control" value="">
    <br>
          Subject:<br>
          <input type="text" name="txtsubject" class="form-control" value="">
    <br>
          Message:<br>
          <textarea name="txtmessage" class="form-control" rows="5" cols="25" required="required" placeholder="Message"></textarea>
          <br>
          <br>
          <input type="submit" class="btn-primary" value="Submit" >
        </form></div>';
        return $form;
    }
 function formOk(){
        return false;
    }
}
4. Create folder name for js file wp-content\plugins\myplugins_shortcode_ajax\js\ajax_call_to_handle_form_submit.js then add this code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jQuery( document ).ready(function() {
    // Handler for .ready() called.
    //
    jQuery(function(){
  jQuery("#green_form").submit(function(event){
   event.preventDefault();
            var formOk = true;
            // do js validation
   jQuery.ajax({
    url:ajax_object.ajax_url,
                type:'POST',
                data: jQuery(this).serialize() + "&action=ajax_do_something", //wp_ajax_nopriv_ajax_do_something, wp_ajax_ajax_do_something
    success:function(response){
     if(response=="true"){
                       //alert('success');
        jQuery("#display_rec").html("<div class='success'><p>Message Success Sent</p></div>")
                    }else{
                        jQuery("#display_rec").html("<div class='fail'>Please input required fields.</div>")
                        //alert('Please input required fields.');
                    }
                }
   });
  });
    });
});
5. css code wp-content\plugins\myplugins_shortcode_ajax\css\ajaxshortcodestyle.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
.form-control {
    display: block;
    width: 100%;
    height: 34px;
    padding: 6px 12px;
    font-size: 14px;
    line-height: 1.428571429;
    color: #555;
    background-color: #fff;
    background-image: none;
    border: 1px solid #ccc;
    border-radius: 4px;
    transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
}
.btn-primary {
    color: #fff;
    background-color: #428bca;
    border-color: #357ebd;
}
.well {
    padding: 19px;
    margin-bottom: 20px;
    background-color: #428bca;
    border: 1px solid #357ebd;color:#fff;
    border-radius: 4px;
    -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
    box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
}
.success, .fail, .information, .attention {
 background-repeat: no-repeat;
 background-position: 10px center;
 height: 40px;
 font-size: 11px;
 line-height: 22px;
 margin-bottom: 20px;
 padding-top: 10px;
 padding-right: 10px;
 padding-bottom: 10px;
 padding-left: 50px;
  
}
/* Succes Notification Box */
.success     {
 background-color: #EBF8D6;
 border: 1px solid #A6DD88;
 color: #539B2D;
 background-image: url(../images/accept00.png);
}
/* Failure Notification Box */
.fail      {
 background-color: #FFECE6;
 border: 1px solid #FF936F;
 color: #842100;
 background-image: url(../images/delete00.png);
}
/* Information Notification Box */
.information    {
 background-color: #D3EEF1;
 border: 1px solid #81CDD8;
 color: #369CAB;
 background-image: url(../images/info0000.png);
}
/* Attention Notification Box */
.attention   {
 background-color: #FFFBCC;
 border: 1px solid #FFF35E;
 color: #C69E00;
 background-image: url(../images/warning0.png);
}
6. Shortcode [shortcode_ajax_form]

Monday, February 4, 2019

How to create wodpress plugins and shortcodes 11 example from simple to complex

How to create wodpress plugins and shortcodes 11 example from simple to complex

List of tutorial
1. How to create contact form with submit mail
2. How to insert record post database table
3. How to insert shortcode google ads
4. How to list recent post
5. How to list recent post in shortcode parameters
6. How to list recent post in content shortcode
7. How to enabling shortcodes in widgets
8. Display wordpress menu in shortcode
9. How to insert A Google Maps shortcode
10. How to embed pdf
11. How to Displaying database table data in shortcodes

A standard header is introduced below to establish your plugin’s presence.
1
2
3
4
5
6
7
8
9
10
<?php
/**
* Plugin Name: My Plugin Name
* Description: A brief description about your plugin.
* Version: 1.0 or whatever version of the plugin (pretty self explanatory)
* Author: Plugin Author's Name
* Author URI: Author's website
* License: A "Slug" license name e.g. GPL12
*/
Create folder under plugins folder wp-content\plugins\myplugins_with_shortcode folder name myplugins_with_shortcode and create php file index.php file
2. Add the code below wp-content\plugins\myplugins_with_shortcode\index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?php
/**
* Plugin Name: Custom Plugins with shortcode
* Description: A custom plugin and shortcode for example.
* Version: 1.0
* Author: Cairocoders
**/
//first example how to create contact form with submit mail
function form_creation(){
 //add submit
 if (isset( $_POST['cmdsubmit'])) {
        $txtname = $_POST['txtname'];
        $txtemail = $_POST['txtemail'];
        $message = $_POST['message'];
  $msg = "Name: $txtname <br/>Email: $txtemail <br/>Message: $message <br/>";
    // send email
  mail("someone@example.com","My subject",$msg);
    }
 $form .= '<div class="well"><p><h5>Simple Contact Form</h5></p><form method = "post">
 Name: <input type="text" name="txtname" class="form-control"><br>
 Email: <input type="email" name="txtemail" class="form-control"><br>
 Message: <textarea name="message" class="form-control"> Enter text here…</textarea><br/>
 <input type="submit" name="cmdsubmit"> '.$msg.'
 </form></div>';
 return "$form";
}
add_shortcode("testform", "form_creation"); //add code //<p>[testform]</p>
//add css file
function wpse_89494_enqueue_scripts() {
 wp_enqueue_style('handlecss',  plugin_dir_url(__FILE__) . 'css/ajaxshortcodestyle.css');
}
//second example how to insert to post
function input_fields( $atts ) {
 //add submit
 if ( isset( $_POST['gg'] ) ) {
        $post = array(
            'post_content' => $_POST['content'],
   'post_status' => 'publish',
            'post_title'   => $_POST['title']
        );
  // Insert the post into the database
    }
    $formpost .= '<form method = "post">
        Post title : <input type="text" name="title"><br/>
        Post Contents : <input type="text" name="content"></br/>
        <input type="submit" name="gg">
    </form>';
 return "$formpost";
}
add_shortcode( 'add_fields', 'input_fields' );
//third example shortcode google ads
function get_adsense($atts) {
    return '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- WordPress hosting -->
<ins class="adsbygoogle"
     style="display:inline-block;width:728px;height:90px"
     data-ad-client="ca-pub-0327511395451286"
     data-ad-slot="6566424785"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>';
}
add_shortcode('adsense', 'get_adsense');
//4th example how to list recent post
function recent_posts_function() {
   query_posts(array('orderby' => 'date', 'order' => 'DESC' , 'showposts' => 1));
   if (have_posts()) :
      while (have_posts()) : the_post();
         $return_string = '<a href="'.get_permalink().'">'.get_the_title().'</a>';
      endwhile;
   endif;
   wp_reset_query();
   return $return_string;
}
add_shortcode('recent_posts', 'recent_posts_function');
//5 example SHORTCODE PARAMETERS
function recent_posts_function2($atts){
   extract(shortcode_atts(array(
      'posts' => 1,
   ), $atts));
   $return_string = '<ul>';
   query_posts(array('orderby' => 'date', 'order' => 'DESC' , 'showposts' => $posts));
   if (have_posts()) :
      while (have_posts()) : the_post();
         $return_string .= '<li><a href="'.get_permalink().'">'.get_the_title().'</a></li>';
      endwhile;
   endif;
   $return_string .= '</ul>';
   wp_reset_query();
   return $return_string;
}
add_shortcode('recent_posts2', 'recent_posts_function2');
//6 example CONTENT IN SHORTCODE [recent-posts posts="5"]This is the list heading[/recent-posts]
function recent_posts_function3($atts, $content = null) {
   extract(shortcode_atts(array(
      'posts' => 1,
   ), $atts));
   $return_string = '<h3>'.$content.'</h3>';
   $return_string .= '<ul>';
   query_posts(array('orderby' => 'date', 'order' => 'DESC' , 'showposts' => $posts));
   if (have_posts()) :
      while (have_posts()) : the_post();
         $return_string .= '<li><a href="'.get_permalink().'">'.get_the_title().'</a></li>';
      endwhile;
   endif;
   $return_string .= '</ul>';
   wp_reset_query();
   return $return_string;
}
add_shortcode('recent_posts3', 'recent_posts_function3');
//7 example ENABLING SHORTCODES IN WIDGETS
add_filter('widget_text', 'do_shortcode');
 
//8 example WORDPRESS MENU
function menu_function($atts, $content = null) {
   extract(
      shortcode_atts(
         array( 'name' => null, ),
         $atts
      )
   );
   return wp_nav_menu(
      array(
          'menu' => $name,
          'echo' => false
          )
   );
}
add_shortcode('menu', 'menu_function');
//9 example A Google Maps shortcode
function googlemap_function($atts, $content = null) {
   extract(shortcode_atts(array(
      "width" => '640',
      "height" => '480',
      "src" => ’
   ), $atts));
   return '<iframe width="'.$width.'" height="'.$height.'" src="'.$src.'&output=embed" ></iframe>';
}
add_shortcode("googlemap", "googlemap_function");
 
//10 example PDF EMBEDDING
function pdf_function($attr, $url) {
   extract(shortcode_atts(array(
       'width' => '640',
       'height' => '480'
   ), $attr));
   return '<iframe src="http://docs.google.com/viewer?url=' . $url . '&embedded=true" style="width:' .$width. '; height:' .$height. ';">Your browser does not support iframes</iframe>';
}
add_shortcode('pdf', 'pdf_function');
// last 11 example Displaying database table data in shortcodes
function listpost_func()
{
 global $wpdb;
    $postshow .= '<div class="recent-posts">';
 $sql_post = $wpdb->get_results("SELECT * FROM wp_posts WHERE post_status='publish'");
 foreach ($sql_post as $rows_post)
 {
  $post_title = $rows_post->post_title; 
   $postshow .= '<h6>'.$post_title.'</h6>';
 }
    $postshow .= '</div>';
 return "$postshow";
}
add_shortcode( 'listpost', 'listpost_func' );
css code wp-content\plugins\myplugins_with_shortcode\css\ajaxshortcodestyle.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.form-control {
    display: block;
    width: 100%;
    height: 34px;
    padding: 6px 12px;
    font-size: 14px;
    line-height: 1.428571429;
    color: #555;
    background-color: #fff;
    background-image: none;
    border: 1px solid #ccc;
    border-radius: 4px;
    transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
}
.btn-primary {
    color: #fff;
    background-color: #428bca;
    border-color: #357ebd;
}
.well {
    padding: 19px;
    margin-bottom: 20px;
    background-color: #428bca;
    border: 1px solid #357ebd;color:#fff;
    border-radius: 4px;
    -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
    box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
}

Thursday, January 31, 2019

How To Create A WordPress Plugin

How To Create A WordPress Plugin

A plugin must contain a bit of meta information which tells WordPress what it is and how to handle it within your website.

A standard header is introduced below to establish your plugin’s presence.



1
2
3
4
5
6
7
8
9
10
<?php
/**
* Plugin Name: My Plugin Name
* Description: A brief description about your plugin.
* Version: 1.0 or whatever version of the plugin (pretty self explanatory)
* Author: Plugin Author's Name
* Author URI: Author's website
* License: A "Slug" license name e.g. GPL12
*/

1 Create folder under plugins folder  wp-content\plugins\myplugins folder name myplugins and create index.php file

2. Add the code below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php
/**
* Plugin Name: Custom Music Reviews
* Description: A custom music review plugin built for example.
* Version: 1.0
* Author: Cairocoders
**/
 
// Register the Custom Music Review Post Type
function register_cpt_music_review() {
    $labels = array(
        'name' => _x( 'Music Reviews', 'music_review' ), //post type is called music_review url https://codex.wordpress.org/Post_Types
        'singular_name' => _x( 'Music Review', 'music_review' ),
        'add_new' => _x( 'Add New', 'music_review' ), //and new link wp-admin/post-new.php?post_type=music_review
        'add_new_item' => _x( 'Add New Music Review', 'music_review' ),
        'edit_item' => _x( 'Edit Music Review', 'music_review' ),
        'new_item' => _x( 'New Music Review', 'music_review' ),
        'view_item' => _x( 'View Music Review', 'music_review' ),
        'search_items' => _x( 'Search Music Reviews', 'music_review' ),
        'not_found' => _x( 'No music reviews found', 'music_review' ),
        'not_found_in_trash' => _x( 'No music reviews found in Trash', 'music_review' ),
        'parent_item_colon' => _x( 'Parent Music Review:', 'music_review' ),
        'menu_name' => _x( 'Music Reviews', 'music_review' ), //left side menu edit.php?post_type=music_review
    );
  
    $args = array(
        'labels' => $labels,
        'hierarchical' => true,
        'description' => 'Music reviews filterable by genre',
        'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'page-attributes' ),
        'taxonomies' => array( 'genres' ),
        'public' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'menu_position' => 5,
        'menu_icon' => 'dashicons-format-audio',
        'show_in_nav_menus' => true,
        'publicly_queryable' => true,
        'exclude_from_search' => false,
        'has_archive' => true,
        'query_var' => true,
        'can_export' => true,
        'rewrite' => true,
        'capability_type' => 'post'
    );
  
    register_post_type( 'music_review', $args );
}
add_action( 'init', 'register_cpt_music_review' );
 
//custom taxonomies to extend themes or plugins
//Register a new Taxonomy using register_taxonomy()
function genres_taxonomy() { //taxonomy is a way to group things together.
    register_taxonomy( //https://codex.wordpress.org/Taxonomies
        'genres', //new taxonomy called genres
        'music_review', //and assigns it to our post type music_review
        array(
            'hierarchical' => true,
            'label' => 'Genres', //edit-tags.php?taxonomy=genres&post_type=music_review
            'query_var' => true,
            'rewrite' => array(
                'slug' => 'genre',
                'with_front' => false
            )
        )
    );
}
add_action( 'init', 'genres_taxonomy');
 
 
// Function used to automatically create Music Reviews page.
function create_music_review_pages()
  {
   //post status and options
    $post = array(
          'comment_status' => 'open',
          'ping_status' =>  'closed' ,
          'post_date' => date('Y-m-d H:i:s'),
          'post_name' => 'music_review',
          'post_status' => 'publish' ,
          'post_title' => 'Music Reviews',
          'post_type' => 'page',
    );
    //insert page and save the id
    $newvalue = wp_insert_post( $post, false );
    //save the id in the database
    update_option( 'mrpage', $newvalue );
  }
// Activates function if plugin is activated
register_activation_hook( __FILE__, 'create_music_review_pages');
 
//Testing
//first create a new page called "Music Reviews" http://localhost/wordpress/music_review/
//Lets create an example music review and see what outputs.

Sunday, January 14, 2018

How to Add Widgets to WordPress Theme’s Footer

How to Add Widgets to WordPress Theme’s Footer

1. Register the footer widget area

Open the functions.php file from the WordPress Theme Editor and search for the following line of code:


register_sidebar

Add the following block of code just below the other sidebar registration code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function twentysixteen_widgets_init() {
 register_sidebar( array(
  'name'          => __( 'Sidebar', 'twentysixteen' ),
  'id'            => 'sidebar-1',
  'description'   => __( 'Add widgets here to appear in your sidebar.', 'twentysixteen' ),
  'before_widget' => '<section id="%1$s" class="widget %2$s">',
  'after_widget'  => '</section>',
  'before_title'  => '<h2 class="widget-title">',
  'after_title'   => '</h2>',
 ) );
 
 register_sidebar( array(
  'name'          => __( 'Content Bottom 1', 'twentysixteen' ),
  'id'            => 'sidebar-2',
  'description'   => __( 'Appears at the bottom of the content on posts and pages.', 'twentysixteen' ),
  'before_widget' => '<section id="%1$s" class="widget %2$s">',
  'after_widget'  => '</section>',
  'before_title'  => '<h2 class="widget-title">',
  'after_title'   => '</h2>',
 ) );
 
 register_sidebar( array(
  'name'          => __( 'Content Bottom 2', 'twentysixteen' ),
  'id'            => 'sidebar-3',
  'description'   => __( 'Appears at the bottom of the content on posts and pages.', 'twentysixteen' ),
  'before_widget' => '<section id="%1$s" class="widget %2$s">',
  'after_widget'  => '</section>',
  'before_title'  => '<h2 class="widget-title">',
  'after_title'   => '</h2>',
 ) );
 register_sidebar( array(
  'name'          => __( 'Content Bottom 3', 'twentysixteen' ),
  'id'            => 'sidebar-4',
  'description'   => __( 'Appears at the bottom of the content on posts and pages.', 'twentysixteen' ),
  'before_widget' => '<section id="%1$s" class="widget %2$s">',
  'after_widget'  => '</section>',
  'before_title'  => '<h2 class="widget-title">',
  'after_title'   => '</h2>',
 ) );
}
add_action( 'widgets_init', 'twentysixteen_widgets_init' );
<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span>

2. Show the footer widget area in your theme

Open your footer.php file and insert the following block of code where you want to show the footer widgets (this will show the 3 footer widget areas):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="footer-sidebar" class="secondary">
<div id="footer-sidebar1">
 <?php
  if(is_active_sidebar('sidebar-2')){
  dynamic_sidebar('sidebar-2');
  }
 ?>
</div>
<div id="footer-sidebar2">
 <?php
 if(is_active_sidebar('sidebar-3')){
 dynamic_sidebar('sidebar-3');
 }
 ?>
</div>
<div id="footer-sidebar3">
 <?php
 if(is_active_sidebar('sidebar-4')){
 dynamic_sidebar('sidebar-4');
 }
 ?>
</div>
</div>

wp_nav_menu change sub-menu class name?

wp_nav_menu change sub-menu class name?

1
from <ul class="sub-menu"> to <ul class="dropdown-menu">

You can use WordPress preg_replace filter (in your theme functions.php file) example:

1
2
3
4
5
function new_submenu_class($menu) {   
    $menu = preg_replace('/ class="sub-menu"/',' class="dropdown-menu" ',$menu);       
    return $menu;     
}
add_filter('wp_nav_menu','new_submenu_class');

Monday, May 1, 2017

Create Custom Shortcode in WordPress Post, Page and Plugin

Create Custom Shortcode in WordPress Post, Page and Plugin

Edit current theme's function.php add this code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function recent_posts_func( $atts, $content = NULL ){
    $content = $content?$content:'Latest Posts';
    $a = shortcode_atts(
        array(
            'posts'=>5
        ),
        $atts
    );
    $args = array('numberposts'=>$a['posts']);
    $recent_posts = wp_get_recent_posts( $args, ARRAY_A );
    echo '<div class="recent-posts">';
    echo '<h1>'.$content.'</h1>';
    foreach($recent_posts as $post){
    ?>
    <div class="updated"><p><?php echo $post['post_title']; ?>. <a href="<?php echo get_permalink($post["ID"]); ?>"><span>Show Details</span>.</a></p></div>
    <?php
    }
    echo '</div>';
}
add_shortcode( 'latestposts', 'recent_posts_func' );

Shortcode content view:

Insert the shortcode through the WordPress admin panel editor, use the below code.

[latestposts posts="2"]Recent Posts[/latestposts]

Related Post