这是我的数据库帮助程序代码,使用 sqflite flutter 库,适用于 Android、iOS 和 MacOS。但我希望我的应用程序也能在 Web 上运行,将这段代码转换为 Web 的最佳方法是什么?
import 'dart:io';
import 'package:excel/excel.dart';
import 'package:flutter/material.dart';
import 'package:frag_store/features/controllers/compounds/create_compound_controller.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static Future<Database> database() async {
return openDatabase(
join(await getDatabasesPath(), 'FragStore.db'),
onCreate: (db, version) async {
await db.execute(
"CREATE TABLE Users(id INTEGER PRIMARY KEY, firstName TEXT, lastName TEXT, gender TEXT, email TEXT, phoneNumber TEXT, password TEXT)",
);
await db.execute(
"CREATE TABLE VendorLogs(id INTEGER PRIMARY KEY, company TEXT, date TEXT, item TEXT, price TEXT, qty TEXT, unit TEXT, notes TEXT)",
);
await db.execute('''
CREATE TABLE Compound (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
ingredientsname TEXT,
ingredient
sqty TEXT,
ingredientsunit TEXT,
qty TEXT,
unit TEXT,
notes TEXT,
specification TEXT,
usage TEXT,
tags TEXT,
fragrancenotes TEXT,
price REAL -- Add price field
)
''');
await db.execute(
"CREATE TABLE Vendors(id INTEGER PRIMARY KEY, name TEXT, phone TEXT, country TEXT, location TEXT, address TEXT, category TEXT, tags TEXT)",
);
await db.execute(
"CREATE TABLE Ingredients(id INTEGER PRIMARY KEY, name TEXT, price TEXT, qty TEXT, unit TEXT, notes TEXT, specification TEXT, usage TEXT, tags TEXT, fragrancenotes TEXT)",
);
await db.execute('''
CREATE TRIGGER update_compound_price
AFTER UPDATE OF price ON Ingredients
BEGIN
UPDATE Compound
SET price = (
SELECT SUM(CAST(Ingredients.price AS REAL) * Ingredients.qty)
FROM Compound c
JOIN Ingredients ON c.ingredientsname = Ingredients.name
)
WHERE Compound.name IN (
SELECT DISTINCT c.name
FROM Compound c
JOIN Ingredients ON c.ingredientsname = Ingredients.name
);
END;
''');
await db.execute('''
CREATE TABLE FragranceNotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
note TEXT
)
''');
await db.execute('''
CREATE TABLE Tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tag TEXT
)
''');
// Add default values to FragranceNotes table
// await db.rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Woody")');
// await db
// .rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Peaceful")');
// await db.rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Bold")');
// // Add default values to Tags table
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Light")');
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Classy")');
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Scented")');
// // Adding Vendor table
},
version: 1,
);
}
static Future<void> insertIngredient(Map<String, dynamic> ingredient) async {
final Database db = await database();
await db.insert(
'Ingredients', // Assuming the table name is Ingredients
ingredient,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<bool> isIngredient(String name) async {
final db = await database();
final result =
await db.rawQuery("SELECT * FROM Ingredients WHERE name = ?", [name]);
return result.isNotEmpty;
}
// Method to fetch data of an ingredient
static Future<Map<String, dynamic>> getIngredientData(String name) async {
final db = await database();
final result =
await db.rawQuery("SELECT * FROM Ingredients WHERE name = ?", [name]);
return result.isNotEmpty ? result.first : {};
}
static Future<void> updateIngredient(
String name,
String price,
String qty,
String notes,
String specification,
String usage,
List<String> tags,
List<String> fragranceNotes) async {
try {
// Open the database
Database db = await openDatabase('FragStore.db');
// Perform the update operation
await db.update(
'Ingredients',
{
'price': price,
'qty': qty,
'notes': notes,
'specification': specification,
'usage': usage,
'tags':
tags.join(', '), // Convert List<String> to comma-separated string
'fragrancenotes': fragranceNotes
.join(', '), // Convert List<String> to comma-separated string
},
where: 'name = ?',
whereArgs: [name],
);
// Close the database
await db.close();
} catch (e) {
throw Exception('Failed to update ingredient: $e');
}
}
static Future<void> updateCompound(
String name,
String price,
String qty,
String notes,
String specification,
String usage,
List<String> tags,
List<String> fragranceNotes) async {
try {
// Open the database
Database db = await openDatabase('FragStore.db');
// Perform the update operation
await db.update(
'Compound',
{
'price': price,
'qty': qty,
'notes': notes,
'specification': specification,
'usage': usage,
'tags':
tags.join(', '), // Convert List<String> to comma-separated string
'fragrancenotes': fragranceNotes
.join(', '), // Convert List<String> to comma-separated string
},
where: 'name = ?',
whereArgs: [name],
);
// Close the database
await db.close();
} catch (e) {
throw Exception('Failed to update compound: $e');
}
}
static Future<List<String>> fetchFragranceNotes() async {
final Database db = await database();
final List<Map<String, dynamic>> maps = await db.query('FragranceNotes');
return List.generate(maps.length, (i) {
return maps[i]['note'];
});
}
static Future<void> insertTag(String tag) async {
final Database db = await database();
await db.insert('Tags', {'tag': tag});
}
static Future<void> insertFragranceNote(String note) async {
final Database db = await database();
await db.insert('FragranceNotes', {'note': note});
}
static Future<List<String>> fetchTags() async {
final Database db = await database();
final List<Map<String, dynamic>> maps = await db.query('Tags');
return List.generate(maps.length, (i) {
return maps[i]['tag'];
});
}
static Future<List<String>> fetchIngredients() async {
final Database db = await database();
final List<Map<String, dynamic>> ingredients = await db.query('Ingredients');
return List.generate(ingredients.length, (i) {
return ingredients[i]['name'];
});
}
static Future<List<String>> fetchCompounds() async {
final Database db = await database();
final List<Map<String, dynamic>> compound = await db.query('Compound');
return List.generate(compound.length, (i) {
return compound[i]['name'];
});
}
static Future<Map<String, dynamic>> fetchQuantityAndUnit(
String selectedTable, TextEditingController selectedItem) async {
Database db = await database();
List<Map<String, dynamic>> result = await db.query(
selectedTable,
where: 'name = ?',
whereArgs: [selectedItem.text],
);
if (result.isNotEmpty) {
// If a record is found, return the quantity and unit
return {'quantity': result.first['qty'], 'unit': result.first['unit']};
} else {
// If no record is found, return default values
return {
'quantity': 0,
'unit':
'default_unit' // You can replace 'default_unit' with your desired default unit
};
}
}
static Future<void> updateQuantityAndUnit(String selectedTable,
String selectedItem, int newQuantity, String newUnit) async {
Database db = await database();
await db.update(
selectedTable,
{'qty': newQuantity, 'unit': newUnit},
where: 'name = ?',
whereArgs: [selectedItem],
);
}
// Method to fetch data of a compound
static Future<Map<String, dynamic>> getCompoundData(String name) async {
final db = await database();
final result =
await db.rawQuery("SELECT * FROM Compound WHERE name = ?", [name]);
return result.isNotEmpty ? result.first : {};
}
// Method to insert compound data into the database
static Future<void> insertCompoundEntry(
Map<String, dynamic> compoundData) async {
final db = await database();
await db.insert('Compound', compoundData);
}
static Future<bool> doesIngredientExist(String name) async {
final Database db = await database();
final List<Map<String, dynamic>> ingredients = await db.query(
'Ingredients', // Assuming the table name is Ingredients
where: 'name = ?',
whereArgs: [name],
);
return ingredients.isNotEmpty;
}
static Future<bool> doesCompoundExist(String name) async {
final Database db = await database();
final List<Map<String, dynamic>> compound = await db.query(
'Compound', // Assuming the table name is Ingredients
where: 'name = ?',
whereArgs: [name],
);
return compound.isNotEmpty;
}
static Future<List<Map<String, dynamic>>> getAllCompounds() async {
final Database db = await database();
return await db.query('Compound');
}
static Future<bool> doesVendorExist(String name) async {
final Database db = await database();
final List<Map<String, dynamic>> vendors = await db.query(
'Vendors',
where: 'name = ?',
whereArgs: [name],
);
return vendors.isNotEmpty;
}
static Future<void> insertUser(Map<String, dynamic> user) async {
final Database db = await database();
await db.insert(
'Users',
user,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<void> insertVendorLog(Map<String, dynamic> log) async {
final Database db = await database();
await db.insert(
'VendorLogs',
log,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<void> deleteUserByPhoneNumber(String phoneNumber) async {
final db = await database();
await db.delete(
'Users',
where: 'phoneNumber = ?',
whereArgs: [phoneNumber],
);
}
static Future<void> deleteCompound(String compoundName) async {
final Database db = await database();
await db.delete(
'Compound',
where: 'name = ?',
whereArgs: [compoundName],
);
}
static Future<void> deleteIngredient(String ingredientName) async {
final Database db = await database();
await db.delete(
'Ingredients',
where: 'name = ?',
whereArgs: [ingredientName],
);
}
static Future<Map<String, dynamic>?> getUserByPhoneAndPassword(
String phoneNumber, String password) async {
final Database db = await database();
final List<Map<String, dynamic>> users = await db.query(
'Users',
where: 'phoneNumber = ? AND password = ?',
whereArgs: [phoneNumber, password],
);
if (users.isNotEmpty) {
return users.first;
} else {
return null;
}
}
static Future<bool> userExistsWithPhoneNumber(String phoneNumber) async {
final Database db = await database();
final List<Map<String, dynamic>> users = await db.query(
'Users',
where: 'phoneNumber = ?',
whereArgs: [phoneNumber],
);
return users.isNotEmpty;
}
static Future<List<Map<String, dynamic>>> getAllUsers() async {
final Database db = await database();
return await db.query('Users');
}
static Future<List<Map<String, dynamic>>> getAllIngredients() async {
final Database db = await database();
return await db.query('Ingredients');
}
static Future<List<Map<String, dynamic>>> getAllCompanyLogs() async {
final Database db = await database();
return await db.query('VendorLogs');
}
static Future<void> exportUsersToExcel() async {
final List<Map<String, dynamic>> users = await getAllUsers();
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = '${documentsDirectory.path}/Users.xlsx';
final file = File(path);
if (await file.exists()) {
await file.delete();
}
final excel = Excel.createExcel();
final sheet = excel['Sheet1'];
sheet.appendRow([
'id',
'firstName',
'lastName',
'gender',
'email',
'phoneNumber',
'password'
]);
for (final user in users) {
sheet.appendRow([
user['id'],
user['firstName'],
user['lastName'],
user['gender'],
user['email'],
user['phoneNumber'],
user['password'],
]);
}
final fileBytes = excel.encode();
await file.writeAsBytes(fileBytes!);
print('Excel file saved to: $path');
}
static Future<void> insertVendor(Map<String, dynamic> vendor) async {
final Database db = await database();
await db.insert(
'Vendors', // Assuming the table name is Vendors
vendor,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<List<Map<String, dynamic>>> getVendorLogs() async {
final Database db = await database();
return await db.query('VendorLogs');
}
static Future<List<Map<String, dynamic>>> getVendorLogsByCompany(
String company) async {
final Database db = await database();
return await db.query(
'VendorLogs',
where: 'company = ?',
whereArgs: [company],
);
}
static Future<List<Map<String, dynamic>>>
getAggregatedVendorLogsByCompanyAndTenure(
String company, String tenure) async {
final Database db = await database();
String groupByClause;
List<dynamic> whereArgs = [company];
String tenureColumn;
if (tenure == 'Monthly') {
groupByClause = "item, strftime('%Y-%m', date)";
tenureColumn = "strftime('%Y-%m', date) AS tenure";
} else if (tenure == 'Yearly') {
groupByClause = "item, strftime('%Y', date)";
tenureColumn = "strftime('%Y', date) AS tenure";
} else {
throw ArgumentError('Invalid tenure: $tenure');
}
return await db.query(
'VendorLogs',
columns: [
'company',
'item',
'unit',
'SUM(price) AS totalPrice',
'SUM(qty) AS totalQuantity',
tenureColumn
],
where: 'company = ?',
whereArgs: whereArgs,
groupBy: groupByClause,
orderBy: 'date DESC',
);
}
static Future<void> exportVendorLogsToExcel() async {
final List<Map<String, dynamic>> logs = await getVendorLogs();
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = '${documentsDirectory.path}/VendorLogs.xlsx';
final file = File(path);
if (await file.exists()) {
await file.delete();
}
final excel = Excel.createExcel();
final sheet = excel['Sheet1'];
sheet.appendRow([
'id',
'company',
'date',
'item',
'price',
'qty',
'unit',
'notes',
]);
for (final log in logs) {
sheet.appendRow([
log['id'],
log['company'],
log['date'],
log['item'],
log['price'],
log['qty'],
log['unit'],
log['notes'],
]);
}
final fileBytes = excel.encode();
await file.writeAsBytes(fileBytes!);
print('Excel file saved to: $path');
}
static Future<void> insertDummyCompounds() async {
final Database db = await database();
await db.transaction((txn) async {
for (int i = 1; i <= 5; i++) {
await txn.insert(
'Compounds',
{'name': 'Compound $i', 'stockAvailable': 10},
);
}
});
}
}
我将 main.dart 修改为如下所示:-
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:frag_store/app.dart';
import 'package:frag_store/features/screens/login/login.dart';
import 'package:path/path.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
import 'package:sqflite/sqflite.dart';
void main() {
// Initialize FFI
if (kIsWeb) {
// Some web specific code there
databaseFactory = databaseFactoryFfiWeb;
}
runApp(App());
}
但是如果我尝试 // 为 Fragran 添加默认值
ceNotes table
// await db.rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Woody")');
// await db
// .rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Peaceful")');
// await db.rawInsert('INSERT INTO FragranceNotes(note) VALUES ("Bold")');
// // Add default values to Tags table
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Light")');
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Classy")');
// await db.rawInsert('INSERT INTO Tags(tag) VALUES ("Scented")');
// // Adding Vendor table using
数据库助手
它给出了一个错误:-错误SqfliteFfiException(sqlite_error:1,,SqliteException(1):执行时,没有这样的列:Woody,SQL逻辑错误(代码1) 导致语句:INSERT INTO FragranceNotes(note) VALUES ("Woody"),参数:}) DatabaseException(SqliteException(1): 执行时,没有这样的 列:Woody,SQL逻辑错误(代码1) 引发语句:INSERT INTO FragranceNotes(note) VALUES ("Woody"),参数:) sql 'INSERT INTO FragranceNotes(note) VALUES ("Woody")' {details: {database: {path: /FragStore.db, id: 1, readOnly: false, singleInstance: true}}} 打开、关闭期间......虽然这段代码适用于 sqflite。
Woody 被视为标识符而不是字符串文字:-
如果在无法解析为标识符但允许使用字符串文字的上下文中使用双引号中的关键字(例如:“key”或“glob”),则该标记将被理解为字符串文字的标识符。 https://sqlite.org/lang_keywords.html
您可能希望考虑使用单引号而不是双引号,如下所示:-
如果在允许标识符但不允许字符串文字的上下文中使用单引号中的关键字(例如:“key”或“glob”),则该标记将被理解为标识符而不是字符串字面意思。
考虑以下演示(使用 SQLite 工具 (Navicat)):-
DROP TABLE IF EXISTS FragranceNotes;
CREATE TABLE FragranceNotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
note TEXT
);
INSERT INTO FragranceNotes(note) VALUES ('Woody');
INSERT INTO FragranceNotes(note) VALUES ("Woody");
SQLite 工具输出消息的结果:-
DROP TABLE IF EXISTS FragranceNotes
> OK
> Time: 0.113s
CREATE TABLE FragranceNotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
note TEXT
)
> OK
> Time: 0.024s
INSERT INTO FragranceNotes(note) VALUES ('Woody')
> Affected rows: 1
> Time: 0.024s
INSERT INTO FragranceNotes(note) VALUES ("Woody")
> no such column: Woody
> Time: 0s
即
一切都按预期进行。
但是,第二次插入,使用双引号而不是单引号作为文字值,会导致您遇到的问题,如所解释的。
即没有这样的专栏:Woody