PHP todo app database setup question

I have a some far a table in mind for username and passwords but not sure how to link their "tasks" to my database in order to save them for when they logout and later re login. Any tips?
316 Replies
ἔρως
ἔρως5mo ago
by id create a table with the user_id you can set it up to automatically update and/or delete all tasks when the user is deleted
Jochem
Jochem5mo ago
I'd do something like this users
+--------+----------+-------------+
| userid | username | password |
+--------+----------+-------------+
| 1 | md | [hash here] |
| 2 | jochem | [hash here] |
+--------+----------+-------------+
+--------+----------+-------------+
| userid | username | password |
+--------+----------+-------------+
| 1 | md | [hash here] |
| 2 | jochem | [hash here] |
+--------+----------+-------------+
tasks
+--------+--------+-------------------+-------+
| taskid | userid | description | done |
+--------+--------+-------------------+-------+
| 1 | 1 | Make an app | FALSE |
| 2 | 1 | Do something else | FALSE |
+--------+--------+-------------------+-------+
+--------+--------+-------------------+-------+
| taskid | userid | description | done |
+--------+--------+-------------------+-------+
| 1 | 1 | Make an app | FALSE |
| 2 | 1 | Do something else | FALSE |
+--------+--------+-------------------+-------+
ἔρως
ἔρως5mo ago
by using a foreign relationship
ZomaTheMasterOfDisaster
good point man im rusty tasks should be a separate table
ἔρως
ἔρως5mo ago
hi rusty, i though you were md yes, because it is an n:n relationship
ZomaTheMasterOfDisaster
how you link two different sqlite files?
ἔρως
ἔρως5mo ago
you don't
Jochem
Jochem5mo ago
it's just two tables in a single sqlite file
ἔρως
ἔρως5mo ago
an sqlite file isn't a single table it's an entire database
ἔρως
ἔρως5mo ago
that's why we told you to use sqlite
ZomaTheMasterOfDisaster
do you have the php create the tables separately or write the code in the file?
ἔρως
ἔρως5mo ago
you can do both you can probably use phynx to do the migrations for you
Jochem
Jochem5mo ago
I'd personally use an external SQLITE tool to set up the database
ἔρως
ἔρως5mo ago
i think it supports sqlite
Jochem
Jochem5mo ago
much easier than doing it with code then you just open the file and the tables already exist migrations are a thing to worry about later
ἔρως
ἔρως5mo ago
there's another easy way: when the file doesn't exist, create it from an sql file
ZomaTheMasterOfDisaster
ive done with it code before on mysql but that was like near 18 years ago
ἔρως
ἔρως5mo ago
so, to upgrade the database, just delete the sqlite3 file
ZomaTheMasterOfDisaster
what tools do you suggest?
ἔρως
ἔρως5mo ago
vscode there are extensions for it, to read sqlite files you have vscode
Jochem
Jochem5mo ago
I usually use the cli to create the file I think
ἔρως
ἔρως5mo ago
i make it in php i use the method i said
ZomaTheMasterOfDisaster
i added the cli tool for sqllite but gotta wait for the system to do a rebuild to add it i did make a database folder in my project structure with a tododb.db file but I guess I should delete it and start over
ἔρως
ἔρως5mo ago
don't call it .db use .sqlite3 it's easier for you to know what it is
ZomaTheMasterOfDisaster
should i have the cli create the whole file? or create it before hand then connect to it in the cli and add the tables?
ἔρως
ἔρως5mo ago
i think that that's too much work i just use the method i said don't create it before make the tool create it
Jochem
Jochem5mo ago
this is what I do. Just create the file, open it with the cli, run the CREATE TABLE commands, and close it again for me, migrations are for databases it would take me more than 30 seconds to recreate from scratch
ZomaTheMasterOfDisaster
this should work because migrations by hand can be tedious
ἔρως
ἔρως5mo ago
yes, but for this, since he's going to do lots of changes, just having an sql file with all the create statements, then delete the file and make php create it automatically is easier you want to add a column? edit the sql file, delete the database ... profit!
ZomaTheMasterOfDisaster
i think with PDO it's just new PDO ("sqlite://database/tododb.sqlite3"); right?
ἔρως
ἔρως5mo ago
yes
ZomaTheMasterOfDisaster
what you think jochem about epic input ?
ἔρως
ἔρως5mo ago
i used that for a project, for very quick prototyping which is what you're doing now you can add data to the sql file as well, by the way
ZomaTheMasterOfDisaster
explain how you do your method epic with detail if you can since php can handle it
Jochem
Jochem5mo ago
It's what I'd do for something bigger than a todo app.
ZomaTheMasterOfDisaster
true im still gonna learn that way for other projects but this seems a bit much
Jochem
Jochem5mo ago
like, you can just use ALTER statements to make one or two tweaks to the database file. There's nothing wrong with the concept though, and it can be handy to keep the sql file anyway just for reference if you ever do delete the database file
ἔρως
ἔρως5mo ago
dude, it's literally this:
$create = !file_exists(...sqlite3);
$pdo = new pdo(...);

if($create) {
$pdo->query(file_get_contents(...sql));
}
$create = !file_exists(...sqlite3);
$pdo = new pdo(...);

if($create) {
$pdo->query(file_get_contents(...sql));
}
this
Jochem
Jochem5mo ago
also remember db files don't go in git in general, so it can be valuable to have a .sql version of the structure so you don't have to recreate the structure from your code later
ἔρως
ἔρως5mo ago
exactly! and, you can take the sql file, run the code anywhere and the database is created automatically this is awful pseudocode, by the way
ZomaTheMasterOfDisaster
change my sqlite3 into a sql file for github?
ἔρως
ἔρως5mo ago
nonono sqlite3 = the database file sql = the file with the sql code to re-create the database file
ἔρως
ἔρως5mo ago
if you need to change anything, you change in the sql file then just delete the database file
Jochem
Jochem5mo ago
you would, at least definitely in a larger project, keep an SQL file with SQL statements that recreates your database from scratch. Basically a structure backup, plus inserts for any rows that are required for your app to function, so that if you rebuild it from scratch six months later, you can just run the file and not have to bother to recreate from scratch. It's not a huge deal for a one-off project with two tables and less than ten columns, but it's useful to have
ZomaTheMasterOfDisaster
sweet the update finished i got the CLI tool
ZomaTheMasterOfDisaster
this what I got so far
ἔρως
ἔρως5mo ago
also, if he ruins the database, he can re-create it
Jochem
Jochem5mo ago
we're in total agreement about the usefulness, I'm just arguing that for two tables with 3 columns each it's potentially a little bit overkill to write migrations
ἔρως
ἔρως5mo ago
oh, im not talking about migrations, but quick destroy and recreate scenarios
ZomaTheMasterOfDisaster
ok so userid is int how big? username and password text?
ἔρως
ἔρως5mo ago
it's sqlite you have int or int big int, small int ... it's just int but i would use bigint unsigned
ZomaTheMasterOfDisaster
im thinking user_id BIG INT UNSIGNED, username VARCHAR(255), password TEXT, task_id BIG INT UNSIGNED, description TEXT, done BOOLEAN? do I need to do anything with foreign key for user_id?
ἔρως
ἔρως5mo ago
primary key index wait, no
Jochem
Jochem5mo ago
I don't think bcrypt produces more than 255 characters, does it? So your password can just be a varchar.
ἔρως
ἔρως5mo ago
actually, you are using sqlite it has a rowid field for you just use that and yes, varchar(255) is enough for a bcrypt password and it is too for an argon2 password
Jochem
Jochem5mo ago
ah hey, actually, I think sqlite doesn't have a varchar type internally. It'll accept it, but it just translates it to TEXT
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL
);
ἔρως
ἔρως5mo ago
sqlite accepts the varchar declaration also, you can drop the user_id
CREATE TABLE users (
username TEXT NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE users (
username TEXT NOT NULL,
password TEXT NOT NULL
);
this is enough sqlite automatically creates a rowid field
Jochem
Jochem5mo ago
that I did not know You could also primary key on the username, technically. It's unique anyway but I personally prefer to be explicit and have it named the way I like
ἔρως
ἔρως5mo ago
yes, this would be a good idea for mysql a unique index for it
ZomaTheMasterOfDisaster
Row id
ἔρως
ἔρως5mo ago
rowid https://www.sqlite.org/rowidtable.html using the rowid is way better than using your own primary key you can still set an index on the username, but i doubt you will have more than 6 rows
ZomaTheMasterOfDisaster
Would email be a better login? Or username
Jochem
Jochem5mo ago
I usually prefer email logins personally one less thing to remember
ἔρως
ἔρως5mo ago
same
ZomaTheMasterOfDisaster
convert username to email?
ἔρως
ἔρως5mo ago
nah
ZomaTheMasterOfDisaster
QLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users ( username TEXT NOT NULL, password TEXT NOT NULL);
sqlite> CREATE TABLE tasks ( taskid INTEGER PRIMARY KEY AUTOINCREMENT, rowid INTEGER, description TEXT, done BOOLEAN );
QLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users ( username TEXT NOT NULL, password TEXT NOT NULL);
sqlite> CREATE TABLE tasks ( taskid INTEGER PRIMARY KEY AUTOINCREMENT, rowid INTEGER, description TEXT, done BOOLEAN );
this what I got so far
ἔρως
ἔρως5mo ago
no no no no no don't create the rowid column
ZomaTheMasterOfDisaster
but wont tasks and user not match on row ids?
Jochem
Jochem5mo ago
this is why I prefer to be explicit with the ID columns
ἔρως
ἔρως5mo ago
NEVER create a rowid column
Jochem
Jochem5mo ago
just create the user_id column INTEGER PRIMARY KEY AUTOINCREMENT on the users table, then reference it with a user_id column on tasks
ἔρως
ἔρως5mo ago
unless you know what you're doing
ZomaTheMasterOfDisaster
ill need to start over
ἔρως
ἔρως5mo ago
i dont like jochem's suggestion, but it's the best option if the rowid confuses you so, just do what he said
Jochem
Jochem5mo ago
what Epic is suggesting is having a user_id column on the tasks table which then references the rowid on users
ἔρως
ἔρως5mo ago
^
Jochem
Jochem5mo ago
you'd join with SELECT * FROM users u LEFT JOIN tasks t ON t.user_id = u.rowid I prefer being explicit, so you can do SELECT * FROM users u LEFT JOIN tasks t USING(user_id) (does that even work in sqlite?)
ZomaTheMasterOfDisaster
both tables are created 🙂 how do we save the file from CLI?
ἔρως
ἔρως5mo ago
it's close enough
Jochem
Jochem5mo ago
don't think you have to, it just writes as you go just .exit
ZomaTheMasterOfDisaster
weird file just shows up as "todoDB" no extension
ἔρως
ἔρως5mo ago
that's because you didn't add an extension?
ZomaTheMasterOfDisaster
would userid in tasks table need an AUTOINCREMENT?
ἔρως
ἔρως5mo ago
no you can't autoincrement 2 columns
ZomaTheMasterOfDisaster
make it NOT NULL?
ἔρως
ἔρως5mo ago
yes
ZomaTheMasterOfDisaster
ok done!
ἔρως
ἔρως5mo ago
what's the create sql for the tables?
ZomaTheMasterOfDisaster
[md89@darknet database]$ sqlite3 todoDB.sqlite3
SQLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password NOT NULL);
sqlite> CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN);
sqlite> .exit
[md89@darknet database]$
[md89@darknet database]$ sqlite3 todoDB.sqlite3
SQLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password NOT NULL);
sqlite> CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN);
sqlite> .exit
[md89@darknet database]$
ἔρως
ἔρως5mo ago
you forgot the unsigned also, you didn't set up the constraint
ZomaTheMasterOfDisaster
:/ what am i missing?
ἔρως
ἔρως5mo ago
all integers must be insigned and you should add a constraint to the tasks table
ZomaTheMasterOfDisaster
dont know how to do that on sqlite everything gets casted to INTEGER according to SQLite
ἔρως
ἔρως5mo ago
yes, but not unsigned
ἔρως
ἔρως5mo ago
just add UNSIGNED
ZomaTheMasterOfDisaster
INTEGER UNSIGNED
CREATE TABLE users (userid INTEGER UNSIGNED PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password NOT NULL);
CREATE TABLE users (userid INTEGER UNSIGNED PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password NOT NULL);
before I press enter
ἔρως
ἔρως5mo ago
no, the other way around also, use bigint
ZomaTheMasterOfDisaster
CREATE TABLE users (userid UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
CREATE TABLE users (userid UNSIGNED BIG INT PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
ἔρως
ἔρως5mo ago
yup, sounds good
ZomaTheMasterOfDisaster
Parse error: AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY looks like you cant
ἔρως
ἔρως5mo ago
integer then
ZomaTheMasterOfDisaster
they changed things
ἔρως
ἔρως5mo ago
🤔 i mean, when you don't use the rowid column, and try to create your own, weird stuff gets weird but, you're right, it's just integer
ZomaTheMasterOfDisaster
CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN);
CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN);
ἔρως
ἔρως5mo ago
yup
ZomaTheMasterOfDisaster
how we add the constraint?
ἔρως
ἔρως5mo ago
by the way, don't use a "done" use an enum for the status because the task can be in many states
ZomaTheMasterOfDisaster
so what data type fits then?
ἔρως
ἔρως5mo ago
it can be paused, done, pending, queued, running, cancelled, error ...
ZomaTheMasterOfDisaster
Each column in an SQLite 3 database is assigned one of the following type affinities:

TEXT
NUMERIC
INTEGER
REAL
BLOB
Each column in an SQLite 3 database is assigned one of the following type affinities:

TEXT
NUMERIC
INTEGER
REAL
BLOB
this project is going to get more complicated
ἔρως
ἔρως5mo ago
status TEXT CHECK( status IN ( ... ) )
ZomaTheMasterOfDisaster
idk how to do that I just went by boolean because Jochem used it why not just... Done or Not Done?
ἔρως
ἔρως5mo ago
you do exactly as i wrote it
ZomaTheMasterOfDisaster
this isnt meant to be a big project just a simple todo app so i can move on to a bigger project
ἔρως
ἔρως5mo ago
i know oh, then just "done" is enough
ZomaTheMasterOfDisaster
im debating on a blog next project one that I actively update with news like a personal ok how we add the constraint?
ἔρως
ἔρως5mo ago
ive never done it in sqlite 🤣 https://www.sqlite.org/foreignkeys.html
ἔρως
ἔρως5mo ago
yup there should be something that lets you delete everything from the tasks when you delete an user and before you think it is overkill: data consistency is never overkill
ZomaTheMasterOfDisaster
got it
[md89@darknet database]$ sqlite3 todoDB.sqlite3
SQLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
sqlite> CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN, FOREIGN KEY(userid) REFERENCES users(userid));
sqlite> .exit
[md89@darknet database]$ sqlite3 todoDB.sqlite3
SQLite version 3.43.2 2023-10-10 12:14:04
Enter ".help" for usage hints.
sqlite> CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
sqlite> CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN, FOREIGN KEY(userid) REFERENCES users(userid));
sqlite> .exit
now im going to eat dinner
ἔρως
ἔρως5mo ago
good luck
ἔρως
ἔρως5mo ago
👋
ZomaTheMasterOfDisaster
so for my models i got this
<?php

namespace models;

class User {
public $username;
public $password;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}
<?php

namespace models;

class User {
public $username;
public $password;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}
<?php

namespace models;

class Task
{
public $description;
protected $done;

public function isDone() {
return $this->done;
}

public function done() {
$this->done = true;
}
}
<?php

namespace models;

class Task
{
public $description;
protected $done;

public function isDone() {
return $this->done;
}

public function done() {
$this->done = true;
}
}
ἔρως
ἔρως5mo ago
why public?
ZomaTheMasterOfDisaster
I think these will talk to those fields? username should be public
ἔρως
ἔρως5mo ago
then add a readonly
Jochem
Jochem5mo ago
also, you can shortcircuit those assignments in the constructor
ἔρως
ἔρως5mo ago
yup
Jochem
Jochem5mo ago
I forget how, but you don't need to do that anymore
ZomaTheMasterOfDisaster
make password protected or private
ἔρως
ἔρως5mo ago
public function __construct(
public string $username,
public string $password,
) {}
public function __construct(
public string $username,
public string $password,
) {}
none: get it from the database and throw it away after use
ZomaTheMasterOfDisaster
oh ok so just take password completely away from the user model?
ἔρως
ἔρως5mo ago
yeah, i wouldn't save any passwords keep it in the database only
Jochem
Jochem5mo ago
yeah, you can just fetch the pw when you need to check it
ἔρως
ἔρως5mo ago
^
ZomaTheMasterOfDisaster
i also added this request for trimming
ἔρως
ἔρως5mo ago
what?
ZomaTheMasterOfDisaster
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
trimming paths down for the browser url
ἔρως
ἔρως5mo ago
why don't you use $_SERVER['PATH_INFO']?
ἔρως
ἔρως5mo ago
this lets you do /index.php/same/path/here
ἔρως
ἔρως5mo ago
that's what i told you make it readonly
ZomaTheMasterOfDisaster
achieves same thing?
ἔρως
ἔρως5mo ago
no, i just explained the difference here
ZomaTheMasterOfDisaster
i changed REQUEST_URI to PATH_INFO
ἔρως
ἔρως5mo ago
you can use both $_SERVER['PATH_INFO'] ?? $_SERVER['REQUEST_URI'] ?? '/'
ZomaTheMasterOfDisaster
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['REQUEST_URI'], $_SERVER['PATH_INFO']), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['REQUEST_URI'], $_SERVER['PATH_INFO']), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
ἔρως
ἔρως5mo ago
that's not what i wrote https://www.php.net/manual/en/function.parse-url.php that will also throw an exception
ZomaTheMasterOfDisaster
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['PATH_INFO'] ?? $_SERVER['REQUEST_URI'] ?? "/", PHP_URL_PATH), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
<?php

namespace helpers;

class Request {
public static function uri() {
return trim(
parse_url($_SERVER['PATH_INFO'] ?? $_SERVER['REQUEST_URI'] ?? "/", PHP_URL_PATH), "/"
);
}

public static function method() {
return $_SERVER['REQUEST_METHOD'];
}
}
ἔρως
ἔρως5mo ago
close, yes
ZomaTheMasterOfDisaster
updated
ἔρως
ἔρως5mo ago
thats better
ZomaTheMasterOfDisaster
very ternary are the models ok for the database?
ἔρως
ἔρως5mo ago
null coalescense i think so
ZomaTheMasterOfDisaster
great! hey trying to load this class variable in for my database it's not liking it
<?php

namespace database;

require '../Config.php';


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE);
}

return $this->pdo;
}
}
<?php

namespace database;

require '../Config.php';


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE);
}

return $this->pdo;
}
}
here's Config
<?php

namespace App;

class Config {
const PATH_TO_SQLITE = './database/todoDB.sqlite3';
}
<?php

namespace App;

class Config {
const PATH_TO_SQLITE = './database/todoDB.sqlite3';
}
ἔρως
ἔρως5mo ago
just use dependency injection
ZomaTheMasterOfDisaster
directory structure
Jochem
Jochem5mo ago
your ./database is refering to the path relative to Config, but the string is used in database/conn.php
ZomaTheMasterOfDisaster
Undefined type 'database\Config' is namespace operate like Go does with thing where it goes by the folder name?
Jochem
Jochem5mo ago
try \Config? (though tbh I'm past the Balmer point and just guessing)
ἔρως
ἔρως5mo ago
you're not following proper psr paths you need to use \database\Config also, the path is wrong it should be ./...sqlite3
Jochem
Jochem5mo ago
doubt that, config isn't part of the database folder or namespace
ἔρως
ἔρως5mo ago
since you want it in the same directory
Jochem
Jochem5mo ago
it's either App\Config or \Config
ZomaTheMasterOfDisaster
so namespace functions kinda like Golang
ZomaTheMasterOfDisaster
everything is in "app" folder
ἔρως
ἔρως5mo ago
namespaces function like paths no, you put them wherever you want but there's standards so, follow them you can save so many headaches just by using composer if not, just have all the classes inside a folder, like "classes" and register a class autoload that takes the namespace path and includes the file
Jochem
Jochem5mo ago
this is what I used to do, yeah never bothered with namespaces
ZomaTheMasterOfDisaster
wait where do I change the path? in the "Config::" or require
ἔρως
ἔρως5mo ago
config
ZomaTheMasterOfDisaster
ok here's new
<?php

namespace App;

class Config {
const PATH_TO_SQLITE = '../app/database/todoDB.sqlite3';


}
<?php

namespace App;

class Config {
const PATH_TO_SQLITE = '../app/database/todoDB.sqlite3';


}
ἔρως
ἔρως5mo ago
yikes! no! todoDB.sqlite3 that's it
ZomaTheMasterOfDisaster
config isn;t in database folder though? is that ok?
ἔρως
ἔρως5mo ago
where's the path going to be used?
ZomaTheMasterOfDisaster
PDO for connection
ἔρως
ἔρως5mo ago
and where is that file?
ZomaTheMasterOfDisaster
inside database folder both sqlite3 file and conn.php inside database
<?php

namespace database;

require '../Config.php';


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:" . App/Config::PATH_TO_SQLITE);
}

return $this->pdo;
}
}
<?php

namespace database;

require '../Config.php';


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:" . App/Config::PATH_TO_SQLITE);
}

return $this->pdo;
}
}
ἔρως
ἔρως5mo ago
if it is inside the same folder, then you can just skip the directory because it's relative to where you use it otherwise, it's just a normal string
ZomaTheMasterOfDisaster
that's weird PDO errored without the "" forward slash
ἔρως
ἔρως5mo ago
use \PDO; at the top
ZomaTheMasterOfDisaster
<?php

namespace database;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite://todoDB.sqlite3");
}

return $this->pdo;
}
}
<?php

namespace database;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite://todoDB.sqlite3");
}

return $this->pdo;
}
}
i deleted the Config.php
ἔρως
ἔρως5mo ago
that's a bad idea, but i get why you did it
ZomaTheMasterOfDisaster
what do you recommend? i dont want "bad" code that means 0 job
ἔρως
ἔρως5mo ago
that's not bad just sub-optimal
ZomaTheMasterOfDisaster
what's optimal?
ἔρως
ἔρως5mo ago
composer
ZomaTheMasterOfDisaster
oh im not using composer for this project just vanilla php
ἔρως
ἔρως5mo ago
why? you want to show you can use the tools, right?
ZomaTheMasterOfDisaster
others suggested it
ἔρως
ἔρως5mo ago
composer and a psr-2 would be the optimal for you, in my opinion
ZomaTheMasterOfDisaster
for this small scale things like composer and laravel are not needed psr
ἔρως
ἔρως5mo ago
whoever said that is bsing you composer isn't about size it's about reusable code
ZomaTheMasterOfDisaster
how do I use composer for this project? i dont want to use laravel
ἔρως
ἔρως5mo ago
dude, nobody is talking about laravel composer is composer laravel is laravel
ZomaTheMasterOfDisaster
ok what we do with composer here?
ἔρως
ἔρως5mo ago
install composer
ZomaTheMasterOfDisaster
i have composer already on my system
ἔρως
ἔρως5mo ago
alright
ZomaTheMasterOfDisaster
im in the PHPTodoAPP directory where whole project sits
ἔρως
ἔρως5mo ago
give me a second to pick a good loader
ἔρως
ἔρως5mo ago
env/dotenv - Packagist
A simple, tiny and lightweight PHP .env loader
ἔρως
ἔρως5mo ago
check this one it just parses the .env file and stores it in a variable but, you can save it into the environment variables, if you wish
ZomaTheMasterOfDisaster
md89@darknet PHPTodoApp]$ composer require env/dotenv
./composer.json has been created
Running composer update env/dotenv
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking env/dotenv (v2.0.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Downloading env/dotenv (v2.0.0)
- Installing env/dotenv (v2.0.0): Extracting archive
Generating autoload files
No security vulnerability advisories found.
Using version ^2.0 for env/dotenv
[md89@darknet PHPTodoApp]$
md89@darknet PHPTodoApp]$ composer require env/dotenv
./composer.json has been created
Running composer update env/dotenv
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking env/dotenv (v2.0.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Downloading env/dotenv (v2.0.0)
- Installing env/dotenv (v2.0.0): Extracting archive
Generating autoload files
No security vulnerability advisories found.
Using version ^2.0 for env/dotenv
[md89@darknet PHPTodoApp]$
ἔρως
ἔρως5mo ago
that's it now, just do require './vendor/autoload.php'; do it in your index.php file
ZomaTheMasterOfDisaster
created index.php inside app folder and added require "vendor/autoload.php";
ἔρως
ἔρως5mo ago
yes, but do ./vendor/autoload.php you then create a .env file that's parseable by the php_ini_parse function https://www.php.net/manual/en/function.parse-ini-file.php
ZomaTheMasterOfDisaster
something like this?
<?php
var_dump(parse_ini_file('.env', false, INI_SCANNER_RAW));
<?php
var_dump(parse_ini_file('.env', false, INI_SCANNER_RAW));
ἔρως
ἔρως5mo ago
that's what the file does but has a little bit of nuance behind https://github.com/sabroan/php-dotenv/blob/main/src/Dotenv.php this is the code
ZomaTheMasterOfDisaster
i see so what I need in .env file just the path to sqlite ffile?
ἔρως
ἔρως5mo ago
besides, showing that you know how to use composer is important too
ZomaTheMasterOfDisaster
outside of localhost address
ἔρως
ἔρως5mo ago
something like this:
PDO_DSN="sqlite3://db.sqlite3"
PDO_DSN="sqlite3://db.sqlite3"
you can actually do something cool af
define('ENV', \Env\Dotenv::toArray(
path: '.env',
strict: false,
));
define('ENV', \Env\Dotenv::toArray(
path: '.env',
strict: false,
));
ZomaTheMasterOfDisaster
PDO_DB = "sqlite3://app/database/todoDB.sqlite3"
PDO_DB = "sqlite3://app/database/todoDB.sqlite3"
ἔρως
ἔρως5mo ago
then, you can just use ENV['PDO_DSN']
ZomaTheMasterOfDisaster
do i need to move my .env inside app folder? right now it's outside
ἔρως
ἔρως5mo ago
no, leave in the same directory as the index.php file
ZomaTheMasterOfDisaster
index.php inside app
ἔρως
ἔρως5mo ago
the index.php where everything is loaded from
ZomaTheMasterOfDisaster
technically i made a goof since index.php is in app folder then it should be <?php require "../vendor/autoload.php"; since ./ puts it inside app still the php intellisense isn't very great on vscode
ἔρως
ἔρως5mo ago
move the vendor folder to the same folder where the index.php file is actually no, you can leave it like that you can also do ../.env, if you want but that's very confusing
ἔρως
ἔρως5mo ago
what i would do is to have index.php, vendor and .env in the same folder
ZomaTheMasterOfDisaster
this is current structure
ἔρως
ἔρως5mo ago
your structure is a huge mess do this it will save you lots of headaches
ZomaTheMasterOfDisaster
yeah everything is in app never was a directory expert they're all in app now
ἔρως
ἔρως5mo ago
good
ἔρως
ἔρως5mo ago
good, good now, you can add a loader to your composer,json file which should be with the rest of the files same as the composer.lock
ZomaTheMasterOfDisaster
yeah im still learning all these little things im just used to php without anything web is just way different now
ἔρως
ἔρως5mo ago
by the way, you can use require __DIR__ . '/vendor/autoload.php'; to make sure you load it from the right place, without using relative paths well, it is the same but you were making a mess, and im helping you to clean it up for you
ZomaTheMasterOfDisaster
<?php

require __DIR__ . "/vendor/autoload.php";
<?php

require __DIR__ . "/vendor/autoload.php";
ἔρως
ἔρως5mo ago
yup
ZomaTheMasterOfDisaster
awesome
ἔρως
ἔρως5mo ago
now, to add a loader
ZomaTheMasterOfDisaster
this is php ive done in case you're interested
ZomaTheMasterOfDisaster
GitHub
GitHub - MD-2016/Anywhere-Bank-Web-Application: A web based banking...
A web based banking application that allows users to make an account and keep track of their money. - GitHub - MD-2016/Anywhere-Bank-Web-Application: A web based banking application that allows use...
ἔρως
ἔρως5mo ago
move all your classes into a single folder that's a monolithic mess do this, and have that folder be in the same folder as the index.php, .env and so on
ZomaTheMasterOfDisaster
that will mess things up model has two classes in it for those respective models
ἔρως
ἔρως5mo ago
dude, just do it
ZomaTheMasterOfDisaster
every file that has the word class in it
ἔρως
ἔρως5mo ago
open your composer.json file, and add this - replace Acme with a name that you don't mind repeating:
"autoload": {
"psr-4": {"Acme\\": "class/"}
}
"autoload": {
"psr-4": {"Acme\\": "class/"}
}
ZomaTheMasterOfDisaster
{
"require": {
"env/dotenv": "^2.0"
},

"autoload" : {
"psr-4": {"Acme\\": "class/"}
}
}
{
"require": {
"env/dotenv": "^2.0"
},

"autoload" : {
"psr-4": {"Acme\\": "class/"}
}
}
ἔρως
ἔρως5mo ago
are you sure you want to use the acme namespace?
ZomaTheMasterOfDisaster
idk what im even doing so i put what you did
ἔρως
ἔρως5mo ago
what that does is "when you see the namespace starting with 'Acme', it loads the file inside the class/ directory"
ZomaTheMasterOfDisaster
my folder is called classes where they all are currently
ἔρως
ἔρως5mo ago
then you have to change the name of it i mean, in the composer file
ZomaTheMasterOfDisaster
so this means all those files need their namespace changed?
ἔρως
ἔρως5mo ago
no yes if i were you, i would change "Acme" to "MD" or something like that
ZomaTheMasterOfDisaster
renamed classes to class folder
ἔρως
ἔρως5mo ago
that works
ZomaTheMasterOfDisaster
so database namespace needs changed to MD/database or just MD/
ἔρως
ἔρως5mo ago
this BUT!!!!!! the folder HAS TO BE CALLED database SAME CAPITALIZATION and the file name MUST BE THE SAME AS THE CLASS NAME
ZomaTheMasterOfDisaster
well everything been moved to one folder and it's name is class this is gonna be a mess
ἔρως
ἔρως5mo ago
now do these changes to every single file trust me, it's not
ZomaTheMasterOfDisaster
conn.php
<?php

namespace database;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:/todoDB.sqlite3");
}

return $this->pdo;
}
}
<?php

namespace database;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
if ($this->pdo == null) {
$this->pdo = new \PDO("sqlite:/todoDB.sqlite3");
}

return $this->pdo;
}
}
ἔρως
ἔρως5mo ago
you have to change the name of the file to SQLConnection.php
ZomaTheMasterOfDisaster
ok what about the namespace?
ἔρως
ἔρως5mo ago
change it as well this is what happens
ZomaTheMasterOfDisaster
to SQLConnection?
ἔρως
ἔρως5mo ago
no same as you put in there
ZomaTheMasterOfDisaster
{
"require": {
"env/dotenv": "^2.0"
},

"autoload" : {
"psr-4": {"MD\\": "class/"}
}
}
{
"require": {
"env/dotenv": "^2.0"
},

"autoload" : {
"psr-4": {"MD\\": "class/"}
}
}
ἔρως
ἔρως5mo ago
yup
ZomaTheMasterOfDisaster
namespace MD\class;
ἔρως
ἔρως5mo ago
this is what happens: MD\database\SQLConnection will load the file class\database\SQLConnection.php you're mapping the namespaces to a folder
ZomaTheMasterOfDisaster
MD\Class is ok for namespace?
ἔρως
ἔρως5mo ago
that will load in class\class\ so, no, it's awful
ἔρως
ἔρως5mo ago
MD\<foldername>\ <-- namespace
ZomaTheMasterOfDisaster
well all those files got moved out of database, helpers, and other folders so im lost now
ἔρως
ἔρως5mo ago
models will be in class\models the namespace is MD\models
ZomaTheMasterOfDisaster
model User.php
<?php

namespace MD\models;

class User {
public readonly string $username;

public function __construct($username)
{
$this->username = $username;
}
}
<?php

namespace MD\models;

class User {
public readonly string $username;

public function __construct($username)
{
$this->username = $username;
}
}
ἔρως
ἔρως5mo ago
that's it
ZomaTheMasterOfDisaster
ok just feels a little unorganized now that models is empty and such
ἔρως
ἔρως5mo ago
why is it empty?
ZomaTheMasterOfDisaster
i moved them from models to class
ἔρως
ἔρως5mo ago
🤦‍♂️ no! you keep the same directory structure
ZomaTheMasterOfDisaster
so keep same directory structure but they're all inside of class folder?
ἔρως
ἔρως5mo ago
you're literally saying "hey, php, the namespace MD is inside the folder class"
ZomaTheMasterOfDisaster
class/models/User.php
ἔρως
ἔρως5mo ago
yes instead of being 5-6 folders inside app those 5-6 are inside class and class is inside app
ZomaTheMasterOfDisaster
since I put SQLConnection inside database folder with the sqlite3 database previously move that folder inside class?
ἔρως
ἔρως5mo ago
everything that has classes goes inside that folder
ZomaTheMasterOfDisaster
ill just move the file then
ἔρως
ἔρως5mo ago
don't do that keep the same directory structure or you will be making a huge salad
ZomaTheMasterOfDisaster
ill make a different directory for DB move SQLConnection inside class
ἔρως
ἔρως5mo ago
as long as it is inside the class directory, do what you want pretend that the class directory is the app directory but only classes go there
ZomaTheMasterOfDisaster
moved SQLConnection inside a folder called dbhelper inside class folder
ἔρως
ἔρως5mo ago
update the namespace
ZomaTheMasterOfDisaster
basically we're doing the same thing Go lang does except changing folder names to match in go you name the folder based on the namespace
ἔρως
ἔρως5mo ago
you are doing the same now
ZomaTheMasterOfDisaster
then go can find the files easier
ἔρως
ἔρως5mo ago
that's the idea the folders are the namespaces the files are the classes also, NO MORE INCLUDES AND REQUIRES if you have any, delete them all except the one inside the index.php that one's required to load composer
ZomaTheMasterOfDisaster
I think this is a good stopping point
ἔρως
ἔρως5mo ago
that's how it is done then, you can do use MD\dbhelper\SQLConnection; and the file is included imediatelly for you and you can just use SQLConnection right there
ZomaTheMasterOfDisaster
Neat So autoloader
ἔρως
ἔρως5mo ago
that's what the autoloader does it automatically loads the classes for you but uses composer to do it for you
ZomaTheMasterOfDisaster
I know ill almost need to make a routes.php
ἔρως
ἔρως5mo ago
thats up to how you organize things you can use klein, for example it's a very old, but really decent router
ἔρως
ἔρως5mo ago
you can handle routes super easily with it
ἔρως
ἔρως5mo ago
https://packagist.org/ <-- you can search for the packages in this website
Packagist
The PHP Package Repository
ἔρως
ἔρως5mo ago
klein/klein - Packagist
A lightning fast router for PHP