本次範例將會解釋如何透過solidity寫出會員管理系統,user可以將自己的資料放在contract上,並且可以更新自己的資料,不過update function,會先判斷user address 是否已經創建過資料,如果user沒有創建過資料會顯示錯誤。
此範例沒有實際做出應用,所以邏輯設計上,不代表每一個環節和元素都得依靠smart contract來處理,或許有更好的解決方法
pragma solidity^0.4.25;
contract USER{
struct User{
string name;
string email;
int gender; // 0= male 1= female
int age;
address addr;
}
address[] allUser;
uint userId; //User[] users 會面臨到一個問題,就是id如何確認,就算用一個參數來記錄id,會是從1開始,而不是從0開始,除非有處理差異值
mapping (address => uint) userToId;
mapping (uint => address) idToAddr;
mapping (address => User) public users;
function create(string _name,string _email,int _gender,int _age) public returns(bool){
userId++;
idToAddr[userId] = msg.sender;
userToId[msg.sender] = userId;
users[msg.sender] = User({
name: _name,
email: _email,
gender: _gender,
age: _age,
addr: msg.sender
});
return true;
}
function readAll() public returns(address[]){
delete allUser;
for(uint i = 1; i <= userId ; i++){
address userAddr = idToAddr[i];
allUser.push(userAddr); //memory type的array不支援push,只有storage才可以
}
return allUser;
}
function update
(
string _name,
string _email,
int _gender,
int _age
)
public
returns(bool)
{
require(userToId[msg.sender] != 0 ,"Can not find your Id");
users[msg.sender] = User({
name: _name,
email: _email,
gender: _gender,
age: _age,
addr: msg.sender
});
return true;
}
function deletes() public returns(bool){
delete users[msg.sender];
return true;
}
}
struct User{
string name;
string email;
int gender;
int age;
address addr;
}
struct
在solidity 裡可以當作是宣告object,其中value的命名,也需要將type放在第一位再來才命名變數名稱
address[] allUser;
int userId;
mapping (address => int) userToId;
mapping (int => address) idToAddr;
mapping (address => User) public users;
這邊有兩個mapping type userToId
和idToAddr
,裡面的key-value是顛倒的,看起來好像能做得事情差不多,不過目的卻不相同,userToId
一個可以判斷address有沒有在記錄過資料,idToAddr
可以搭配int userId
目前累積的user總量透過迴圈將所有在contract儲存過資料的address放在address[] allUser
顯現出來
function create(string _name,string _email,int _gender,int _age) public returns(bool){...}
name、email、gender、age
,將輸入的參數記錄在contract。只要執行create() function
都會累計userId
記錄目前user總數量,並且和執行function的address,分別放進userToId[msg.sender] = userId
和idToAddr[userId] = msg.sender
,再來,msg.sender
也就是執行function的address,會放在key的位置,輸入的參數則放進在value位置的User Struct,完成資料儲存,成功後回傳true。
function readAll() public returns(address[]){...}
mapping users
來查看該address的資料。比較特別的是,在function body 裏頭的for迴圈i
的宣告,在javascript可能習慣使用var
,不過在solidity是要宣告數字的type,所以寫法是for(uint i = 0; i < 99;i++)
,將所有找到的address都push到address[] allUser
,最後再回傳目前已經填過資料的address list。只要每次執行readAll() function allUser
的狀態都會是有value的狀態,所以readAll() function執行後第一件事是要先將allUser
清空,好讓後面填入資料不會有重複的狀態發生。
function update
(
string _name,
string _email,
int _gender,
int _age
)
public
returns(bool){...}
require(condition,"return err msg")
在solidity為判斷的語法,只要條件不符合require內的設定,EVM就會回傳ERROR,以當前的合約例子解釋require(userToId[msg.sender] != 0 ,"Can not find your Id" )
,是判斷mapping userToId[msg.sender]
的value不應該為0,如果執行function的address還沒將資料輸入進這個contract,ID會是0,接著EVM會回傳錯誤並加入提示訊息Can not find your Id,然後回傳剩餘的gas。在solidity,更改struct type內的資料,以小弟目前所知,是以覆蓋的方式進行更改,而且不能只更改struct內某一個數值,在更改過程一樣得輸入其他欄位的資料;以這個合約例子來看,如果只想要更改age,除了要針對age的欄位輸入新的數值,也必須針對其他欄位輸入不會更動的舊數值。這個部分小弟覺得是比較麻煩的,或許有更好的方法,如果有找到,會在補充。
function deletes() public returns(bool){...}}
delete
會將所有變數的值初始化,以目前的例子來看delete users[msg.sender]
,users[msg.sender]
這個msg.sender
會保留,value的User struct資料結構也會保留,不過struct的內部數值就會初始化。