Skip to main content
Back to Projects
EuroTech Web logo

EuroTech Web

Corporate Website

EuroTech Web architecture diagram EuroTech Web Laravel 10 · PHP 8.1 — dört dilli tanıtım ve başvuru sitesi (arşiv) 2024-10 → 2025-11 · aktif geliştirme kapandı Portalın kamuya açık yüzü; ikisi ayrı uygulamadır. Ziyaretçi aday öğrenci · veli · kurum dört ayrı dilde gelir localize ara katmanı dil URL'in ilk parçasından okunur tek rota ağacı, dört çıktı Blade görünümleri 69 şablon · 6 controller 109 PHP ve Blade kaynak dosyası Başvuru formu Aday bilgilerini bırakır; kayıt veritabanına yazılır ve e-posta ile bildirilir. Bot koruması için captcha ucu ayrı rotada. Adayın kendisi portala buradan geçer. Kampanya kaydı Reklam tarafından gelen ziyaretçi ayrı bir modelde tutulur; sayfa içeriği değişmez, kaynak bilgisi kaybolmaz. İki model: kullanıcı ve kampanya. Çerez ve yönlendirme Onay durumu ayrı bir controller'da; ölçüm kodu onay gelmeden yüklenmez. Eski dil yolları kalıcı yönlendirmeyle taşındı. 355 commit boyunca URL'ler korundu. Neden arşiv Bu iki uygulama bugün yazdığım işin teknik yönünü temsil etmiyor: sunucu tarafında şablon üreten bir PHP monoliti, otomatik test yok, tip güvencesi yok. Portfolyoda durmalarının sebebi büyüklükleri değil, ne öğrettikleri. Buradan taşınan iki alışkanlık Birincisi: çok dilli bir sitede dil, çeviri dosyasından önce rota tasarımının konusudur — sonradan eklenirse URL'ler bozulur. İkincisi: yayındaki bir adresi asla sessizce kaybetme; taşıyacaksan kalıcı yönlendirmeyle taşı.
The archived four-language Laravel 10 site that served as the portal's public face: 69 Blade templates, 6 controllers. Locale is read from the first URL segment; one route tree produces four outputs. The application form writes the lead to the database and notifies by e-mail, ad traffic is stored in a separate campaign model, and measurement code does not load before cookie consent. Old locale paths were moved with permanent redirects.

EuroTech's public face: a four-language marketing and application site. Each language has its own addresses, so an applicant who searches lands on the page in their own language, and the source of a visitor arriving from an ad is kept until the form is sent. Same organisation as the portal, but a separate application. Archived — active development ended at the close of 2025.

Technologies Used

  • Laravel 10
  • PHP 8.1
  • Blade
  • MySQL
  • SendGrid

Key Features

  • Four languages — one route tree, the language read from the first URL segment
  • Applicant form and campaign source recording
  • A verification endpoint on a separate route for bot protection
  • Cookie consent in its own controller; analytics do not load before consent
  • Old language paths preserved through permanent redirects

Development Time

14 months

Problem

The organisation's public face had one job: to carry an applicant arriving in one of four languages to the point where they leave their details. The difficulty here is not translation but route design. In a multilingual site, adding a language later breaks addresses and loses the search visibility accumulated up to that point. The second issue is source tracking: where a visitor arriving from an advert came from should not disappear when the form is submitted — otherwise which channel works is simply unknown. The third is analytics and cookies: the analytics code had to stay unloaded until consent was given.

Users

Three visitors. The applicant: arrives in one of four languages, browses programme and content pages, and eventually fills in the application form. The parent or institution: reads the same content looking for reassurance. The organisation's own team: sees the incoming application and the campaign source — that part runs on the portal side.

My Role

As developer, the whole site: route and language structure, pages, application and campaign flow, cookie and analytics layer.

Architecture

A server-rendered site on Laravel 10. The language is defined as the first segment of the route group and read by a middleware: the middleware compares the incoming value against the four supported languages, falls back to the default if it is invalid, and installs the language as a default parameter for all subsequent URL generation. That way a single route tree produces output in four languages and templates do not have to carry the language. The data side is narrow: two models — user and campaign. The application form writes a record to the database and sends an email notification; bot protection sits on a separate verification route. Cookie consent lives in its own controller, and whether the analytics code loads depends on that consent. Old language paths are preserved through permanent redirects.

Key Decisions

  1. Language was treated as a routing matter, not a translation matter

    The language is the first segment of the route group and is validated by middleware, falling back to the default if invalid. Because the language is also installed as a default parameter for URL generation, templates do not have to carry it — a link written anywhere comes out in the right language. That is what lets four languages live in one route tree.

    Trade-offThe path segments are shared across all four languages; only the prefix changes, the segments themselves are not translated. For search visibility that is a weaker choice than localised paths. In VAP Turizm I solved the same problem the other way round: there the paths themselves change with the language, at the cost of three definitions for every new page.

  2. The campaign source is kept in its own model

    Where a visitor arriving from an advert came from should not disappear when the form is submitted. Keeping the source in a separate model rather than mixing it into page content means the source record survives content changes.

    Trade-offThe source is only captured on first contact; if a visitor returns through another channel the first record stands and last-touch information is not kept. So the record answers "where did they hear about us", not "which channel converted".

  3. Analytics load after consent

    Consent state is managed in a separate controller, and whether the analytics code enters the page depends on it. Analytics do not load before consent; this is a compliance decision made at the cost of measurement accuracy.

    Trade-offVisitors who do not consent never appear in the analytics, so reported traffic stays below real traffic. That difference was accepted.

  4. Published addresses were preserved through permanent redirects

    The language structure changed over time and links to old addresses kept arriving. Rather than being deleted, old paths were put behind permanent redirects, so external links and search-result entries did not break.

    Trade-offThe redirect table grows over time and nothing is ever removed; each redirect is a small maintenance burden and a rule evaluated on every request. There is no measurement showing which redirect still receives traffic.

Stack Rationale

The site was written on the same framework as the portal, and that was deliberate: when two applications of the same organisation share a language, a deployment style and an operating model, maintenance runs on one mental model. Server-rendered templates were enough for a site consisting of marketing content and a form — carrying a separate frontend application and an API contract would not have paid for itself. The site and the portal were nevertheless kept as separate applications: having a public surface share a deployment with a closed portal would have widened the attack surface for no reason.

Security

The only public write path is the application form, protected by a verification endpoint on its own route. Outside that form there is no endpoint through which a visitor can write to the database. Cookie consent is managed in a separate controller and third-party analytics code does not enter the page before consent. Because the site and the portal are separate applications, compromising the public surface does not mean access to portal data — that separation is the most valuable part of this design.

Performance

There is no stored, date-stamped measurement output for this site, so no score is written here. Structurally, pages are produced on the server and sent as full pages; no application layer is carried on the client. Because analytics load only after consent, visitors who decline also carry no third-party script weight.

Results

Active development
2024-10 → 2025-11 · 355 commits
Source files
109 PHP and Blade files — the app, resources, routes and database directories
Structure
6 controllers · 69 Blade views · 2 models · 4 migrations
Languages
Four languages, one route tree
Counting note
This repository has the third-party library folder under version control; the count was taken only from the directories I wrote

What I Would Do Differently

The clearest lesson I carried out of here is path localisation. On this site only the language prefix changes while the segments themselves are identical in all four languages; in VAP Turizm I solved the same problem the other way round and split the paths by language. The second lesson is redirects: not silently losing a published address is the one thing that cannot be repaired later. What I would do differently is the measurement side: the redirect table grew but nothing shows which rule still receives traffic, so no redirect can be safely deleted. I closed the same gap on the inventory side in Ops Panel — without seeing how long a record has been in a given state, you cannot decide which one to clean up.

Archive

Let's find where to start

Tell me what you want to build; I'll tell you up front how long it takes and what the sensible first step is.