Immobilien & User hinzugefügt

This commit is contained in:
2025-11-08 18:56:59 +01:00
parent cba9aef518
commit 320f2f30af
11 changed files with 1094 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Controller;
use App\Entity\Immobilie;
use App\Repository\ImmobilieRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/immobilien')]
class ImmobilieController extends AbstractController
{
#[Route('/', name: 'app_immobilie_index')]
public function index(ImmobilieRepository $repository): Response
{
$immobilien = $repository->findVerfuegbare();
return $this->render('immobilie/index.html.twig', [
'immobilien' => $immobilien,
]);
}
#[Route('/{id}', name: 'app_immobilie_show', requirements: ['id' => '\d+'])]
public function show(Immobilie $immobilie): Response
{
return $this->render('immobilie/show.html.twig', [
'immobilie' => $immobilie,
]);
}
#[Route('/suche', name: 'app_immobilie_suche')]
public function suche(ImmobilieRepository $repository): Response
{
return $this->render('immobilie/suche.html.twig');
}
}

325
src/Entity/Immobilie.php Normal file
View File

@@ -0,0 +1,325 @@
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Enum\ImmobilienTyp;
use App\Repository\ImmobilieRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: ImmobilieRepository::class)]
#[ORM\Table(name: 'immobilien')]
#[ORM\HasLifecycleCallbacks]
#[ApiResource]
class Immobilie
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'immobilien')]
#[ORM\JoinColumn(nullable: false)]
#[Assert\NotNull]
private User $verwalter;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\NotBlank]
#[Assert\Length(min: 5, max: 255)]
private string $adresse;
#[ORM\Column(type: 'decimal', precision: 10, scale: 2)]
#[Assert\NotBlank]
#[Assert\Positive]
private float $preis;
#[ORM\Column(type: 'decimal', precision: 8, scale: 2)]
#[Assert\NotBlank]
#[Assert\Positive]
private float $flaeche;
#[ORM\Column(type: 'boolean')]
private bool $garage = false;
#[ORM\Column(type: 'integer')]
#[Assert\NotBlank]
#[Assert\Positive]
private int $zimmer;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Range(min: 1800, max: 2100)]
private ?int $baujahr = null;
#[ORM\Column(type: 'string', enumType: ImmobilienTyp::class)]
private ImmobilienTyp $typ;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $beschreibung = null;
#[ORM\Column(type: 'boolean')]
private bool $verfuegbar = true;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\PositiveOrZero]
private ?int $balkonFlaeche = null;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\PositiveOrZero]
private ?int $kellerFlaeche = null;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Min(0)]
#[Assert\Max(10)]
private ?int $etage = null;
#[ORM\Column(type: 'string', length: 100, nullable: true)]
private ?string $heizungstyp = null;
#[ORM\Column(type: 'decimal', precision: 6, scale: 2, nullable: true)]
#[Assert\PositiveOrZero]
private ?float $nebenkosten = null;
#[ORM\Column(type: 'datetime')]
private \DateTimeInterface $createdAt;
#[ORM\Column(type: 'datetime')]
private \DateTimeInterface $updatedAt;
public function __construct()
{
$this->createdAt = new \DateTime();
$this->updatedAt = new \DateTime();
$this->typ = ImmobilienTyp::WOHNUNG;
}
#[ORM\PreUpdate]
public function setUpdatedAtValue(): void
{
$this->updatedAt = new \DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getAdresse(): string
{
return $this->adresse;
}
public function setAdresse(string $adresse): self
{
$this->adresse = $adresse;
return $this;
}
public function getPreis(): float
{
return $this->preis;
}
public function setPreis(float $preis): self
{
$this->preis = $preis;
return $this;
}
public function getFlaeche(): float
{
return $this->flaeche;
}
public function setFlaeche(float $flaeche): self
{
$this->flaeche = $flaeche;
return $this;
}
public function getGarage(): bool
{
return $this->garage;
}
public function isGarage(): bool
{
return $this->garage;
}
public function setGarage(bool $garage): self
{
$this->garage = $garage;
return $this;
}
public function getZimmer(): int
{
return $this->zimmer;
}
public function setZimmer(int $zimmer): self
{
$this->zimmer = $zimmer;
return $this;
}
public function getBaujahr(): ?int
{
return $this->baujahr;
}
public function setBaujahr(?int $baujahr): self
{
$this->baujahr = $baujahr;
return $this;
}
public function getTyp(): ImmobilienTyp
{
return $this->typ;
}
public function setTyp(ImmobilienTyp $typ): self
{
$this->typ = $typ;
return $this;
}
public function getBeschreibung(): ?string
{
return $this->beschreibung;
}
public function setBeschreibung(?string $beschreibung): self
{
$this->beschreibung = $beschreibung;
return $this;
}
public function isVerfuegbar(): bool
{
return $this->verfuegbar;
}
public function setVerfuegbar(bool $verfuegbar): self
{
$this->verfuegbar = $verfuegbar;
return $this;
}
public function getBalkonFlaeche(): ?int
{
return $this->balkonFlaeche;
}
public function setBalkonFlaeche(?int $balkonFlaeche): self
{
$this->balkonFlaeche = $balkonFlaeche;
return $this;
}
public function getKellerFlaeche(): ?int
{
return $this->kellerFlaeche;
}
public function setKellerFlaeche(?int $kellerFlaeche): self
{
$this->kellerFlaeche = $kellerFlaeche;
return $this;
}
public function getEtage(): ?int
{
return $this->etage;
}
public function setEtage(?int $etage): self
{
$this->etage = $etage;
return $this;
}
public function getHeizungstyp(): ?string
{
return $this->heizungstyp;
}
public function setHeizungstyp(?string $heizungstyp): self
{
$this->heizungstyp = $heizungstyp;
return $this;
}
public function getNebenkosten(): ?float
{
return $this->nebenkosten;
}
public function setNebenkosten(?float $nebenkosten): self
{
$this->nebenkosten = $nebenkosten;
return $this;
}
public function getCreatedAt(): \DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): \DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getVerwalter(): User
{
return $this->verwalter;
}
public function setVerwalter(User $verwalter): self
{
$this->verwalter = $verwalter;
return $this;
}
/**
* Berechnet den Preis pro Quadratmeter
*/
public function getPreisProQm(): float
{
if ($this->flaeche > 0) {
return round($this->preis / $this->flaeche, 2);
}
return 0;
}
/**
* Berechnet die Gesamtfläche inkl. Balkon und Keller
*/
public function getGesamtflaeche(): float
{
$gesamt = $this->flaeche;
if ($this->balkonFlaeche) {
$gesamt += $this->balkonFlaeche;
}
if ($this->kellerFlaeche) {
$gesamt += $this->kellerFlaeche;
}
return $gesamt;
}
}

View File

@@ -10,6 +10,8 @@ use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use App\Enum\UserRole;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
@@ -47,10 +49,14 @@ class User
#[ORM\Column(type: 'datetime')]
private \DateTimeInterface $createdAt;
#[ORM\OneToMany(targetEntity: Immobilie::class, mappedBy: 'verwalter', orphanRemoval: true)]
private Collection $immobilien;
public function __construct()
{
$this->createdAt = new \DateTime();
$this->role = UserRole::USER;
$this->immobilien = new ArrayCollection();
}
public function getId(): ?int
@@ -101,4 +107,28 @@ class User
$this->createdAt = $createdAt;
return $this;
}
/**
* @return Collection<int, Immobilie>
*/
public function getImmobilien(): Collection
{
return $this->immobilien;
}
public function addImmobilie(Immobilie $immobilie): self
{
if (!$this->immobilien->contains($immobilie)) {
$this->immobilien->add($immobilie);
$immobilie->setVerwalter($this);
}
return $this;
}
public function removeImmobilie(Immobilie $immobilie): self
{
$this->immobilien->removeElement($immobilie);
return $this;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Enum;
enum ImmobilienTyp: string
{
case WOHNUNG = 'wohnung';
case HAUS = 'haus';
case GRUNDSTUECK = 'grundstueck';
case GEWERBE = 'gewerbe';
case BUERO = 'buero';
public function getLabel(): string
{
return match($this) {
self::WOHNUNG => 'Wohnung',
self::HAUS => 'Haus',
self::GRUNDSTUECK => 'Grundstück',
self::GEWERBE => 'Gewerbe',
self::BUERO => 'Büro',
};
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace App\Repository;
use App\Entity\Immobilie;
use App\Enum\ImmobilienTyp;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Immobilie>
*/
class ImmobilieRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Immobilie::class);
}
/**
* Find available properties
*/
public function findVerfuegbare(): array
{
return $this->createQueryBuilder('i')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('verfuegbar', true)
->orderBy('i.createdAt', 'DESC')
->getQuery()
->getResult();
}
/**
* Find properties by type
*/
public function findByTyp(ImmobilienTyp $typ): array
{
return $this->createQueryBuilder('i')
->andWhere('i.typ = :typ')
->setParameter('typ', $typ)
->orderBy('i.preis', 'ASC')
->getQuery()
->getResult();
}
/**
* Find properties within price range
*/
public function findByPreisRange(float $minPreis, float $maxPreis): array
{
return $this->createQueryBuilder('i')
->andWhere('i.preis BETWEEN :minPreis AND :maxPreis')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('minPreis', $minPreis)
->setParameter('maxPreis', $maxPreis)
->setParameter('verfuegbar', true)
->orderBy('i.preis', 'ASC')
->getQuery()
->getResult();
}
/**
* Find properties within area range
*/
public function findByFlaecheRange(float $minFlaeche, float $maxFlaeche): array
{
return $this->createQueryBuilder('i')
->andWhere('i.flaeche BETWEEN :minFlaeche AND :maxFlaeche')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('minFlaeche', $minFlaeche)
->setParameter('maxFlaeche', $maxFlaeche)
->setParameter('verfuegbar', true)
->orderBy('i.flaeche', 'ASC')
->getQuery()
->getResult();
}
/**
* Find properties with garage
*/
public function findMitGarage(): array
{
return $this->createQueryBuilder('i')
->andWhere('i.garage = :garage')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('garage', true)
->setParameter('verfuegbar', true)
->orderBy('i.preis', 'ASC')
->getQuery()
->getResult();
}
/**
* Find properties by minimum number of rooms
*/
public function findByMinZimmer(int $minZimmer): array
{
return $this->createQueryBuilder('i')
->andWhere('i.zimmer >= :minZimmer')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('minZimmer', $minZimmer)
->setParameter('verfuegbar', true)
->orderBy('i.zimmer', 'ASC')
->getQuery()
->getResult();
}
/**
* Get average price per sqm by type
*/
public function getAveragePreisProQmByTyp(ImmobilienTyp $typ): float
{
$result = $this->createQueryBuilder('i')
->select('AVG(i.preis / i.flaeche) as avgPreisProQm')
->andWhere('i.typ = :typ')
->andWhere('i.verfuegbar = :verfuegbar')
->setParameter('typ', $typ)
->setParameter('verfuegbar', true)
->getQuery()
->getSingleScalarResult();
return round((float) $result, 2);
}
/**
* Search properties by address
*/
public function searchByAdresse(string $search): array
{
return $this->createQueryBuilder('i')
->andWhere('i.adresse LIKE :search')
->setParameter('search', '%' . $search . '%')
->orderBy('i.createdAt', 'DESC')
->getQuery()
->getResult();
}
}