- Budapesti Műszaki Szakképzési Centrum
- Neumann János Informatikai Technikum
- Szakképesítés neve: Szoftverfejlesztő és -tesztelő
- száma: 5-0613-12-03
- Budapest 2022
Fejlesztő csapat:
- Tóth Péter Pál @Rohampingvin
- Csehi Roland Álmos @SunyiUborka
- Kis Ábrahám @kiss533
Konzulens:
- Várkonyi Tibor Zoltán
Manapság sajnos kevés rendszeren fut a dos-os operációs rendszer, pedig rengeteg érdekes ötlet és gondolat valósult meg ezen a rendszeren keresztül, például akkortájt rengeteg szórakoztató vagy hasznos alkalmazást hoztak létre, amiket napjainkban már nem tudunk élvezni. Az idő múlásával tönkre mennek a régi meghajtók, hordozható tárolók, és teljesen elvesznek ezek a szoftverek, mivel nincsenek a felhőbe mentve.
Ezért szerettünk volna létrehozni egy webalkalmazást, ahol ezeket a szoftvereket tárolni lehet, emellett ezen műfaj kedvelői is le- és fel tudják tölteni saját munkájukat, majd azokat értékelhetik vagy hozzászólhatnak egymás alkotásaihoz. Ezáltal a hibákat jelezhetik, sőt, ha szeretnék, még ki is tárgyalhatják a gondolataikat egy Fórumon vagy kérdezhetnek, ha problémába ütköznek. Mivel web alkalmazásról beszélünk, akárhol elérhetik azt az interneten keresztül bármilyen olyan eszközről, amin futtatható egy dos emulátor.
Mobiltelefonon is használható a webalkalmazás, mivel webes mivoltából adódóan reszponzívan bárhol a megadott eszközön megfelelően működik, viszont nem mindenhol futtathatóak ezek a szoftverek, így csak megadott funkciókat tudunk implementálni ezeken az eszközökön.
- php 8.0
- javascript
- Jetbrains Phpstorm
- Docker
- mysql 8.0-20.04_beta
- PhpMyadmin
Fontos, hogy a docker fusson; ezek után az operációs rendszertől függően elindítjuk, ha Windows rendszerünk van akkor install.bat-ot, ha pedig linux verziónk van akkor intsall.sh file-t futtassunk.
Hogy ha a docker image megfelelően fut, akkor localhost:8881-en elérjük az oldalunkat, localhost:8882-ön pedig a mysql adatbázis kezelőt.
Adatbázis felhasználó neve: laravel
Jelszó: password
docker-compose exec php php artisan make:migration create_name_table
Az adatbázis kapcsolatát a frontend-del MVC keret rendszerrel oldottuk meg.
A modell foglalkozik az adatbázis kapcsolatával, ezáltal könnyen elérjük az adatokat. A modelleket az app/models útvonalon találjuk meg.
A modellben különböző kapcsolatokat is létre lehet hozni, hogy egy megadott táblából mit kapjon meg a modellünk.
class User extends Authenticatable
{
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'username',
'email',
'password',
'user_image'
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
class Program extends Model
{
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'user_id',
'program_file',
'program_image',
'type_name',
'developer',
'release_date',
'name',
'description'
];
}
class Category extends Model
{
protected $primaryKey = "name";
protected $fillable = ['name'];
public $timestamps = false;
public function programs() {
return $this->belongsToMany(Program::class , 'program_categories');
}
}
class Role extends Model
{
protected $keyType = 'string';
public $timestamps = false;
}
class Thread extends Model
{
protected $fillable = ['id' , 'program_id' , 'title' , 'description'];
public function posts() {
return $this->hasMany(Post::class , 'thread_id');
}
}
class Post extends Model
{
protected $fillable = [
'id',
'thread_id',
'user_id',
'title',
'description'
];
public function comments() {
return $this->hasMany(Comment::class , 'post_id');
}
}
class Comment extends Model
{
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = ['id' , 'post_id' , 'user_id' , 'text'];
public function post() {
return $this->belongsTo(Post::class, 'post_id');
}
}
class Type extends Model
{
protected $primaryKey = "name";
protected $fillable = ['name'];
public $timestamps = false;
public function programs() {
return $this->hasMany(Program::class , 'program_id');
}
}
A controller végzi a felhasználó kéréseinek kezelését és kiszolgálását. Más néven vezérlőnek is hívhatjuk, így tudnak kommunikálni a nézetek és a modelek között.
A controller fileokat az app/http/controllers útvonalon találjuk meg.
A controller azért felel, hogy megfelelő adatokkal be lehessen jelentkezni és majd kijelentkezni.
class AuthController extends Controller
{
public function authenticate(LoginRequest $request)
{
$data = $request->validated();
if(!Auth::attempt($data)){
$request->session()->flash("danger", "Hibás felhasználónév vagy jelszó");
return redirect()->route("home");
}
return redirect()->route("home");
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route("home");
}
}
A register az előre megírt validátorral ellenőrzi a megfelelő adatokat, majd ha ez sikeres volt, akkor a jelszót titkosítja és elmenti az adatbázisba és végül átírányít a kezdőoldalra, ahol bejelentkezhet a felhasználó.
class RegisterController extends Controller
{
public function create()
{
return view("auth.create");
}
public function store(RegisterStoreRequest $request)
{
$data = $request->validated();
$data["password"] = Hash::make($data["password"]);
User::create($data);
return redirect()->route("home");
}
}
SiteController irányít minket az oldalak között.
class SiteController extends Controller
{
public function index()
{
return view('dosarc.home');
}
public function home()
{
Gate::authorize("create-belep");
return view('dosarc.home');
}
public function show(Program $program) {
Gate::authorize("create-belep");
return view('dosarc.show', [
'data' => $program,
'title' => $program['name']
]);
}
public function upload() {
Gate::authorize("create-belep");
return view('dosarc.upload');
}
public function forum() {
Gate::authorize("create-belep");
return view('dosarc.forum');
}
public function profile() {
Gate::authorize("create-belep");
return view('dosarc.profile');
}
}
Itt töltjük fel az adatbázisba a profile képeket és megy végbe a jelszóváltoztatás.
class UserController extends Controller
{
public function updateImage(UpdateUserImageRequest $request)
{
$data = $request->validated();
$fileimage = $data['user_image']->store('user_image');
$data['user_image'] = $fileimage;
Auth::user()->update($data);
return redirect()->back();
}
public function updatePassword(UpdatePasswordRequest $request)
{
$data = $request->validated();
if (!Hash::check($data['old_password'], Auth::user()->getAuthPassword())){
$request->session()->flash('bad_password', 'Hibás a jelenlegi jelszó!');
return redirect()->back();
}
$data['password'] = Hash::make($data['password']);
Auth::user()->update($data);
return redirect()->back();
}
}
A ProgramController oldja meg a programok adatainak tárolását, megjelenítését és törlését.
Törölni és szerkeszteni, csak megfelelő jogokkal lehetséges.
class ProgramController extends Controller
{
public function index() {
return Program::all();
}
public function show($id) {
return Program::findOrFail($id);
}
public function store(ProgramStoreRequests $request) {
Gate::authorize("create-belep");
$data = $request->validated();
$filename = $data['program_file']->store('program_file');
$fileimage = $data['program_image']->store('program_image');
$data['user_id'] = Auth::user()->id;
$data['program_file'] = $filename;
$data['program_image'] = $fileimage;
Program::create($data);
return redirect()->back();
}
public function update(ProgramStoreRequests $request , $id) {
Gate::authorize("admin-role");
Program::update($request->validated());
}
public function destroy($id) {
Gate::authorize("admin-role");
Program::delete($id);
}
}
A feltöltött fájlokat a Storage/app útvonalra linkeljük.
Ezt a config/filesystem tehetjük meg; először az adattömb kulcsot kell megadni, aztán a megadjuk a nevét a mappának, ahol tárolni szeretnénk a fájlt.
'links' => [
public_path('user_image') => storage_path('app/user_image'),
public_path('program_file') => storage_path('app/program_file'),
public_path('program_image') => storage_path('app/program_image'),
],
Az útvonalak megadása a routes/web.php fájlon belül lehetséges.
A route függvénnyel crud műveleteket használunk, amivel visszakérhetünk egy vagy több adatot, frissíthetünk, létrehozhatunk és törölhetünk is, megadhatunk egy url címet is akár, amire hivatkozni tudunk. Azután vesszővel elválasztva adhatunk át adatokat egy controllerből és annak egy action meghívásával; emellett ezt el is nevezhetjük a könnyebb meghívás érdekében.
Route::get('/', [\App\Http\Controllers\SiteController::class, 'index'])
->name('home');
docker-compose exec php php artisan make:request name
Ezeket a requesteket az app/http/requests mappába tudjuk generálni és saját meghívási szabályokat hozhatunk létre.
Ehhez előbb meg kell vizsgálni, hogy a user a request szabálynak megfelelő requestet küld-e a modelek felé.
class LoginRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
"username" => ["required"],
"password" => ["required"]
];
}
public function messages() {
return [
'username.required' => 'A felhasználónév megadása kötelező!',
'password.required' => 'A jelszó megadása kötelező!',
];
}
}
A web applikációban található rest api routeok a routes/api.php fájlban lehet megtalálni és ez ugyanúgy működik, mint a web routing, de azzal ellentétben az api route statless, vagyis nem tárol el plusz információt.
route::get('/threads', [\App\Http\Controllers\ThreadController::class, 'index'])
->name('threads.index');
route::get('/threads/{threads}', [\App\Http\Controllers\ThreadController::class, 'show'])
->name('threads.show');
route::post('/threads', [\App\Http\Controllers\ThreadController::class, 'store'])
->name('threads.store');
route::put('/threads/{threads}', [\App\Http\Controllers\ThreadController::class, 'update'])
->name('threads.update');
route::delete('/threads/{threads}', [\App\Http\Controllers\ThreadController::class, 'destroy'])
->name('threads.destroy');
Az api controllerekben kell kezelni a https requesteket.
class ThreadController extends Controller
{
public function index()
{
Thread::all();
}
public function store(ForumRequest $request)
{
Gate::authorize("create-belep");
Thread::create($request->validated());
}
public function show($id)
{
return Thread::findOrFail($id);
}
public function update(Request $request, $id)
{
Gate::authorize("admin-role");
Thread::update($request->validate());
}
public function destroy($id)
{
Gate::authorize("admin-role");
Thread::delete($id);
}
}
A frontend a view-kból állnak és lehetőséget ad webes felületeket készíteni. A resources/views mappában találjuk meg ezeket a nézeteket és ide is kell létrehozni. A laravel view-kat blade tag-gel kell létrehozni, mint pl: index.blade.php, így használni tudjuk a laraveles formokat.
A regisztrációhoz kell egy felhasználónév, ami maximum 16 karakter lehet, és egy jelszó, aminek minimum 8 karakternek kell lennie. Ezeken felül szükség van egy email cím-re is.
Meg kell adnunk a felhasználónevünk és jelszavunk.
Az első, a kis ház, a főmenü oldalának a gombja.
A második az alkalmazás-feltöltés oldal gombja.
A harmadik a fórumkészítő oldalra vezető gomb.
A negyedik a profile szerkesztő oldalra vezető gomb.
Az utolsó a kijelentkezés gomb.
Tudnunk kell a jelszó cseréjéhez a régi jelszavunk és kétszer meg kell adni az új jelszavat, aminek szintén minimum 8 karakternek kell lennie.
Kötelező kitölteni a program neve mezőt, a kategóriát kiválasztani, feltölteni egy képet, megjelölni a típust, felölteni a kívánt fájlt és kijelölni a megjelenés napját, de nem kötelező kitölteni a készítő sort és a leírást.
2.1.6 Forum létrehozása
Kötelező kitölteni az inputot a fórum létrehozásához.
public function test_connecting()
{
$response = $this->get('/');
$response->assertStatus(200);
}
public function test_register(){
$response = $this->post('/register',[
'username' => 'alma',
'email' => '[email protected]',
'password' => 'almaalma',
'password_confirmation' => 'almaalma'
]);
$response->assertRedirect('/');
}
public function test_login(){
$response = $this->post('/login',[
'username' => 'alma',
'password'=> 'almaalma'
]);
$response->assertRedirect('/');
}
public function test_profile_image_update(){
Storage::fake('public');
$this->json('put', '/profile/image', [
'user_image' => $file = UploadedFile::fake()->image('random.jpg')
]);
$this->assertEquals('user_image/' . $file->hashName(), UploadedFile::latest()->first()->file);
Storage::disk('public')->assertExists('user_image/' . $file->hashName());
}
public function test_password_change(){
$url = route('user.update-password');
$values = [
'old_password'=>'adminadmin',
'password'=>'adminadmin',
'password_confirmation' => 'adminadmin',
];
$response = $this->post($url,$values);
$this->assertDatabaseHas(User::class, $values);
$response->assertRedirect('profile');
}
public function test_program_upload(){
Storage::fake('public');
$response = $this->put( '/dosarc', [
'name'=> 'game',
'program_image' => $file_image = UploadedFile::fake()->image('random.jpg'),
'developer' => "",
'type_name' => 'Szoftver',
'category_name' => '',
'program_file' => $program_file = UploadedFile::fake()->create('file.zip'),
'release_date' => '',
'description' => ''
]);
$this->assertEquals('user_image/' . $file_image->hashName(), UploadedFile::latest()->first()->file_image);
$this->assertEquals('program_file/' . $program_file->hashName(), UploadedFile::latest()->first()->program_file);
Storage::disk('public')->assertExists('user_image/' . $file_image->hashName());
$response->assertRedirect('/dosarc');
}
public function test_forum_create(){
$response = $this->get("/forum", [
'title' => 'game',
'title' => 'game for lövölde',
'thread' => 'lövölde jatek',
'description' => 'lövöldözös jatékocska',
'comment_text' => ' 10kill'
]);
$response->assertRedirect('/forum');
}