visit
If you are looking for a personal tutor to speed you up with web3 development, kindly .
Again, if you’re going to work on a blockchain project for either a company or a client, there may still be a need to remove some unnecessary records which is why you should ACE your Solidity CRUD skills now.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Blog {
address public owner;
uint256 public activePostCounter = 0;
uint256 public inactivePostCounter = 0;
uint256 private postCounter = 0;
mapping(uint256 => address) public delPostOf;
mapping(uint256 => address) public authorOf;
mapping(address => uint256) public postsOf;
enum Deactivated { NO, YES }
struct PostStruct {
uint256 postId;
string title;
string description;
address author;
Deactivated deleted;
uint256 created;
uint256 updated;
}
PostStruct[] activePosts;
PostStruct[] inactivePosts;
event Action (
uint256 postId,
string actionType,
Deactivated deleted,
address indexed executor,
uint256 created
);
modifier ownerOnly(){
require(msg.sender == owner, "Owner reserved only");
_;
}
constructor() {
owner = msg.sender;
}
function createPost(
string memory title,
string memory description
) external returns (bool) {
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
postCounter++;
authorOf[postCounter] = msg.sender;
postsOf[msg.sender]++;
activePostCounter++;
activePosts.push(
PostStruct(
postCounter,
title,
description,
msg.sender,
Deactivated.NO,
block.timestamp,
block.timestamp
)
);
emit Action (
postCounter,
"POST CREATED",
Deactivated.NO,
msg.sender,
block.timestamp
);
return true;
}
function updatePost(
uint256 postId,
string memory title,
string memory description
) external returns (bool) {
require(authorOf[postId] == msg.sender, "Unauthorized entity");
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
activePosts[i].title = title;
activePosts[i].description = description;
activePosts[i].updated = block.timestamp;
}
}
emit Action (
postId,
"POST UPDATED",
Deactivated.NO,
msg.sender,
block.timestamp
);
return true;
}
function showPost(
uint256 postId
) external view returns (PostStruct memory) {
PostStruct memory post;
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
post = activePosts[i];
}
}
return post;
}
function getPosts() external view returns (PostStruct[] memory) {
return activePosts;
}
function getDeletedPost() ownerOnly external view returns (PostStruct[] memory) {
return inactivePosts;
}
function deletePost(uint256 postId) external returns (bool) {
require(authorOf[postId] == msg.sender, "Unauthorized entity");
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
activePosts[i].deleted = Deactivated.YES;
activePosts[i].updated = block.timestamp;
inactivePosts.push(activePosts[i]);
delPostOf[postId] = authorOf[postId];
delete activePosts[i];
delete authorOf[postId];
}
}
postsOf[msg.sender]--;
inactivePostCounter++;
activePostCounter--;
emit Action (
postId,
"POST DELETED",
Deactivated.YES,
msg.sender,
block.timestamp
);
return true;
}
function restorDeletedPost(
uint256 postId,
address author
) ownerOnly external returns (bool) {
require(delPostOf[postId] == author, "Unmatched Author");
for(uint i = 0; i < inactivePosts.length; i++) {
if(inactivePosts[i].postId == postId) {
inactivePosts[i].deleted = Deactivated.NO;
inactivePosts[i].updated = block.timestamp;
activePosts.push(inactivePosts[i]);
delete inactivePosts[i];
authorOf[postId] = delPostOf[postId];
delete delPostOf[postId];
}
}
postsOf[author]++;
inactivePostCounter--;
activePostCounter++;
emit Action (
postId,
"POST RESTORED",
Deactivated.NO,
msg.sender,
block.timestamp
);
return true;
}
}
Step 1: The smart contract structure
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract Blog {
// code goes here...
}
Step 2: Defining Contract Variables
address public owner;
uint256 public activePostCounter = 0;
uint256 public inactivePostCounter = 0;
uint256 private postCounter = 0;
The owner will hold the deployer’s account and activePostCounter
holds a number of available posts. inactivePostCounter
keeps track of the deleted post, just in case, it needs to be recovered. postCounter
holds the total number of posts in the smart contract, but it is private.
Step 3: Defining the Mappings
mapping(uint256 => address) public delPostOf;
mapping(uint256 => address) public authorOf;
mapping(address => uint256) public postsOf;
authorOf()
is a mapped variable that accepts a postId
and returns the address of the post author. Whereas postsOf()
keeping track of an author’s posts, and delPostOf() serves as a recycling bin for each author.
Step 4: Defining an Enumerable
enum Deactivated { NO, YES }
Step 5: Defining the Struct
struct PostStruct {
uint256 postId;
string title;
string description;
address author;
Deactivated deleted; // We are using the enum here
uint256 created;
uint256 updated;
}
PostStruct[] activePosts;
PostStruct[] inactivePosts;
We specified in the above PostStruct
that a post should have an Id, title, description, author, a deleted key, time of creation, and time of update.
Step 6: Defining an Event
event Action (
uint256 postId,
string actionType,
Deactivated deleted,
address indexed executor,
uint256 created
);
Step 7: Defining modifier
modifier ownerOnly(){
require(msg.sender == owner, "Owner reserved only");
_;
}
Step 8: Defining the Constructor
constructor() {
owner = msg.sender;
}
Step 9: The createPost()
function
function createPost(
string memory title,
string memory description
) external returns (bool) {
// Checks for empty string
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
// Performs computations
postCounter++;
authorOf[postCounter] = msg.sender;
postsOf[msg.sender]++;
activePostCounter++;
// Adds post to array
activePosts.push(
PostStruct(
postCounter,
title,
description,
msg.sender,
Deactivated.NO,
block.timestamp,
block.timestamp
)
);
// Emits a created event
emit Action (
postCounter,
"POST CREATED",
Deactivated.NO,
msg.sender,
block.timestamp
);
// Returns success
return true;
}
Third, it adds the post into the activePosts
using the PostStruct
defined earlier.
Step 10: The updatePost()
function.
function updatePost(
uint256 postId,
string memory title,
string memory description
) external returns (bool) {
// Checks for empty string
require(authorOf[postId] == msg.sender, "Unauthorized entity");
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
// Changes post record
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
activePosts[i].title = title;
activePosts[i].description = description;
activePosts[i].updated = block.timestamp;
}
}
// Emits a updated event
emit Action (
postId,
"POST UPDATED",
Deactivated.NO,
msg.sender,
block.timestamp
);
// Returns success
return true;
}
This function operates like a createPost()
function. The difference is that when checking for an empty string, the updatePost()
function ensures that the person updating the function is the post owner (author).
Step 11: The showPost()
function
function showPost(
uint256 postId
) external view returns (PostStruct memory) {
PostStruct memory post;
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
post = activePosts[i];
}
}
return post;
}
This simply returns to our post by recursively searching for a match in the activePost
array.
Step 12: The getPosts()
function.
function getPosts() external view returns (PostStruct[] memory) {
return activePosts;
}
Step 13: The getDeletedPost()
function
function getDeletedPost()
ownerOnly external view returns (PostStruct[] memory) {
return inactivePosts;
}
Step 14: The deletePost()
function
function deletePost(uint256 postId) external returns (bool) {
// check if post belong to user
require(authorOf[postId] == msg.sender, "Unauthorized entity");
// find and delete post
for(uint i = 0; i < activePosts.length; i++) {
if(activePosts[i].postId == postId) {
activePosts[i].deleted = Deactivated.YES;
activePosts[i].updated = block.timestamp;
inactivePosts.push(activePosts[i]);
delPostOf[postId] = authorOf[postId];
delete activePosts[i];
delete authorOf[postId];
}
}
// Recallibrate counters
postsOf[msg.sender]--;
inactivePostCounter++;
activePostCounter--;
// Emits event
emit Action (
postId,
"POST DELETED",
Deactivated.YES,
msg.sender,
block.timestamp
);
// Returns success
return true;
}
Please take note that delete behavior is not done like in a normal database, rather we specified two arrays; activePosts
and inactivePosts
. Whenever we deleted a post, it will be moved to the inactivePosts
array, and only the blog owner can restore it at the request of the post author.
Step 15: The Post Restore Function
function restorDeletedPost(
uint256 postId,
address author
) ownerOnly external returns (bool) {
// checks if post exists in users recycle bin
require(delPostOf[postId] == author, "Unmatched Author");
// Finds and restore the post to the author
for(uint i = 0; i < inactivePosts.length; i++) {
if(inactivePosts[i].postId == postId) {
inactivePosts[i].deleted = Deactivated.NO;
inactivePosts[i].updated = block.timestamp;
activePosts.push(inactivePosts[i]);
delete inactivePosts[i];
authorOf[postId] = delPostOf[postId];
delete delPostOf[postId];
}
}
// Recallibrates counter to reflect restored post
postsOf[author]++;
inactivePostCounter--;
activePostCounter++;
// Emits a restoration event
emit Action (
postId,
"POST RESTORED",
Deactivated.NO,
msg.sender,
block.timestamp
);
// resturns success
return true;
}
Whenever a post is restored, it is moved from the inactivePosts
array to the activePosts
array.
I want to inform you of my private tutoring classes for those who want to jump into the web3 space. If you’re interested, please .