Uw startup opbouwen meerdere domeinen draaien

Wat je gaat creëren

Deze zelfstudie maakt deel uit van de Bouw je Startup met PHP-serie op Envato Tuts +. In deze serie begeleid ik je door het opstarten van een startup van concept naar realiteit met behulp van mijn Meeting Planner app als een realistisch voorbeeld. Elke stap die ik doe, zal ik de Meeting Planner-code vrijgeven als open-source voorbeelden waar je van kunt leren. Ik zal ook opstartgerelateerde zakelijke problemen aanpakken zodra deze zich voordoen.

Lancering van een tweede webdomein

Het vergroten van het bewustzijn en het gebruik van Meeting Planner is op dit moment mijn grootste uitdaging. Zonder veel input is het moeilijk om het product beter te maken, en zonder snelle groei is het moeilijk om investeerders aan te trekken.

Ik was bang dat het merk Meeting Planner ertoe zou kunnen leiden dat mensen het sociale nut van de app verkeerd begrijpen, zoals het plannen van vriendelijke bijeenkomsten, datums en feesten.

Natuurlijk is het kiezen van namen beperkt door de beschikbaarheid van het domein en / of het budget dat je moet investeren in het kopen van alternatieven. Meeting Planner leek op dat moment het beste.

Onlangs merkte ik dat SimplePlanner.io beschikbaar was, dus ik heb het geregistreerd en ben begonnen het domein te integreren met de bestaande Meeting Planner-service. 

Er zijn een aantal verschillende benaderingen voor het toevoegen van domeinen aan een op Yii2 gebaseerde toepassing. In de tutorial van vandaag zal ik de eenvoudigste, lopende domeinen op dezelfde codebasis behandelen.

Als u Meeting Planner nog niet hebt uitgeprobeerd, kunt u uw eerste vergadering plannen bij Simple Planner. Ik neem wel deel aan de opmerkingen hieronder, dus vertel me wat je denkt! Je kunt me ook bereiken via Twitter @reifman. Ik ben vooral geïnteresseerd als u nieuwe functies of onderwerpen voor toekomstige zelfstudies wilt voorstellen.

Ter herinnering: alle code voor Meeting Planner en Simple Planner is geschreven in het Yii2 Framework voor PHP. Als je meer wilt weten over Yii2, bekijk dan onze parallelle serie Programming With Yii2.

Voordat ik aan de slag ga, wil ik graag ingaan op een aspect van de uitdaging om een ​​startup te bouwen.

Een kijkje in het startup-leven

Ik heb in het weekend aan deze aflevering gewerkt, een "voordeel" van #StartupLife. Ik wilde hier een paar leuke dingen over vertellen. 

Ten eerste betekent #StartupLife vaak dat je geen tijd hebt om IKEA Furniture af te werken, alleen de grootste lade:

Ten tweede betekent het ook werken bij coffeeshops op leuke plaatsen ... Vijf Point Roasters in Portland, Oregon hebben toevallig een naburige bellenmachine (of mensen ademen daar bellen in):

Nu, terug naar de focus van de tutorial van vandaag ...

Meerdere domeinen implementeren in Yii

Met het geavanceerde sjabloon Yii2 kunt u een aantal sites in één codestructuur uitvoeren. Ik heb de front-end boom gebruikt om Meeting Planner en de back-end tree te bouwen om de administratieve suite met hulpmiddelen voor de service te bouwen. Vandaag zal ik me echter concentreren op het lanceren van een ander domein bovenop de bestaande front-endboom - en alle kleine en grote complexiteiten die hierbij horen. 

In een toekomstige aflevering zal ik bouwlocaties behandelen op een derde of vierde boom, bijvoorbeeld een REST API of een gerelateerde opstart voor Meeting Planner (ja, er staat een spannend idee op stapel).

Ik veronderstelde dat het vrij eenvoudig zou zijn (geen woordspeling bedoeld) om Simple Planner te lanceren, maar het duurde uiteindelijk een paar dagen werk.

Het eenvoudige werk was de basisconfiguratie van de server en een aantal code-afhandeling met de Yii-tree. Aangezien Meeting Planner een e-mailintensieve service is (uitnodigingen voor vergaderingen, bevestigingen, aankondigingen, updates, enz.), Was het belangrijk om deze e-mails te verzenden met het domein waaruit vergaderingen werden gemaakt, ofwel SimplePlanner.io of MeetingPlanner.io.

Ik wilde ook dat er een eerste visuele differentiatie tussen de twee sites zou zijn.

Laten we beginnen, en ik zal geleidelijk een aantal van de complexiteiten die ik tegenkwam laten zien.

Configuratie op basis van het domeinadres

Wanneer mensen via browser-verzoeken op onze site aankomen, hebben we een centrale manier nodig in Yii om alle uitgevoerde code te instrueren welke service moet worden getoond: Meeting Planner of Simple Planner?

In /frontend/config/main.php is er een helaas bootstrap-configuratie (omdat deze overlapt met Bootstrap), die code zal uitvoeren aan het begin van de frameaanroep:

 'mp-frontend', 'naam' => 'Meeting Planner', 'basePath' => dirname (__ DIR__), 'bootstrap' => ['log', '\ common \ components \ SiteHelper'], 'controllerNamespace' = > 'frontend \ controllers', 

Naast het aanroepen van logboekregistratie hierboven, heb ik een component gemaakt om naam te geven SiteHelper:

Dus SiteHelper heeft alle code om de service aan te passen op basis van welke site wordt uitgevoerd. Bijvoorbeeld:

private function commonMeetingPlanner () Yii :: $ app-> params ['site'] ['id'] = SiteHelper :: SITE_MP; Yii :: $ app-> params ['site'] ['domain'] = 'meetingplanner.io'; Yii :: $ app-> params ['site'] ['url'] = 'https://meetingplanner.io'; Yii :: $ app-> params ['site'] ['title'] = Yii :: t ('frontend', 'Meeting Planner'); Yii :: $ app-> params ['site'] ['mtg'] = Yii :: t ('frontend', 'Meetings'); Yii :: $ app-> params ['site'] ['img'] = rand (2,3); Yii :: $ app-> params ['site'] ['navbar'] = 'navbar-inverse'; Yii :: $ app-> params ['site'] ['email_logo'] = 'https://meetingplanner.io/img/email-logo-mp.gif'; Yii :: $ app-> params ['site'] ['ga'] = 'UA-37244292-18';  private function commonSimplePlanner () Yii :: $ app-> params ['site'] ['id'] = SiteHelper :: SITE_SP; Yii :: $ app-> params ['site'] ['domain'] = 'simpleplanner.io'; Yii :: $ app-> params ['site'] ['url'] = 'https://simpleplanner.io'; Yii :: $ app-> params ['site'] ['title'] = Yii :: t ('frontend', 'Simple Planner'); Yii :: $ app-> params ['site'] ['mtg'] = Yii :: t ('frontend', 'Meetups'); Yii :: $ app-> params ['site'] ['img'] = rand (0,1); Yii :: $ app-> params ['site'] ['navbar'] = 'navbar-default'; Yii :: $ app-> params ['site'] ['email_logo'] = 'https://simpleplanner.io/img/email-logo-sp.gif'; Yii :: $ app-> params ['site'] ['ga'] = 'UA-37244292-21'; 

Deze functies veranderen variabelen, URL's en strings op basis van de gevraagde service. Deze worden aangeroepen door de SiteHelper :: init () functie:

commonMeetingPlanner (); Yii :: $ app-> params ['site'] ['url'] = 'http: // localhost: 8888 / mp /'; Yii :: $ app-> params ['site'] ['ga'] = "; else if (stristr ($ baseUrl, '/ sp /')! == false) // local sp $ this-> commonSimplePlanner (); Yii :: $ app-> params ['site'] ['url'] = 'http: // localhost: 8888 / sp /'; Yii :: $ app-> params ['site'] [ 'ga'] = ";  else if (stristr ($ baseUrl, 'simple')! == false) // simpleplanner.io $ this-> commonSimplePlanner ();  else // standaard meetingplanner.io $ this-> commonMeetingPlanner ();  ouder :: init (); 

De bovenstaande functies hebben ook voorrang op instellingen bij het bellen vanaf localhost: 8888 ontwikkelingssites.

Meeting Planner is SITE_MP, Simple Planner is SITE_SP, en SITE_FD is mijn geheim van jou (voorlopig).

Unieke homepage-verschijning

Voor nu besloot ik om het uiterlijk van Simple Planner (SP) en Meeting Planner (MP) snel te veranderen door de twee standaard navigatiebalken van Bootstrap 3.0 te gebruiken.

U merkt boven MP-gebruik Yii :: $ app-> params ['site'] ['navbar'] = 'navbar-default'; en SP gebruikt 'NavBar-inverse';.

In /frontend/views/layouts/home.php en main.php worden ze op deze manier toegepast:

 Yii :: $ app-> params [ 'website'] [ 'title'].' voorvertoning', //' brandUrl '=> Yii :: $ app-> homeUrl,' options '=> [' class '=> Yii :: $ app-> params [' site '] [' navbar '].' navbar-fixed-top ',], 

Hiermee worden de twee verschillende navbar-kleurenschema's gemaakt:

Maar hoe zit het met de coverafbeeldingen? Ik licentie twee speelse sociale afbeeldingen voor SP en twee serieuze professionele afbeeldingen voor MP. 

De afbeeldingen worden willekeurig geroteerd afhankelijk van de actieve service, waarbij afbeeldingsbestandsnamen numeriek eindigen als 0, 1, 2 of 3, bijvoorbeeld /img/home/home-#.jpg.

Yii :: $ app-> params ['site'] ['img'] = rand (2,3);

Hier is de layout-code home.php die dit op de gekozen afbeelding toepast:

 beginBody ()?> 

Als u de startpagina's voor SimplePlanner.io of MeetingPlanner.io ververst, ziet u de afbeeldingen oscilleren.

Tekst, afbeeldingen en koppelingen bijwerken

Variabelen van SiteHelper hierboven helpen bij het aanpassen van tekstlabels op de hele site. En in de toekomst kan ik dit uitvoeriger doen:

Yii :: $ app-> params ['site'] ['title'] = Yii :: t ('frontend', 'Meeting Planner'); Yii :: $ app-> params ['site'] ['mtg'] = Yii :: t ('frontend', 'Meetings'); 

Hoewel MP dingen Meetings noemt, kon ik SP wereldwijd veranderen om de meer sociale zin Meetups te gebruiken:

Yii :: $ app-> params ['site'] ['title'] = Yii :: t ('frontend', 'Simple Planner'); Yii :: $ app-> params ['site'] ['mtg'] = Yii :: t ('frontend', 'Meetups'); 

Services configureren

Meeting Planner maakt gebruik van veel verschillende services om planning te leveren. Het initialiseren kostte de meeste tijd.

Google Analytics en Search Console

Google maakt het een beetje moeilijk om meerdere domeinen te gebruiken met Analytics, dus heb ik ze gewoon in verschillende accounts verdeeld. De SiteHelper stelt de GA-code in:

Yii :: $ app-> params ['site'] ['ga'] = 'UA-37244292-18';

Vervolgens worden deze ingesteld in de lay-out weergaven Home.php en Main.php:

Er is ook een domeininstelling voor Google's Search Console die ik heb geconfigureerd bij mijn registrar (zie hieronder):

OAuth-aanmelding

En aangezien ik sociale login via OAuth voor Facebook, Google en LinkedIn heb toegevoegd, moest ik al deze services over het domein SimplerPlanner.io vertellen. Google en LinkedIn zijn het meest verwarrend omdat elke zoekopdrachtargumentatie moet worden geregistreerd bij die genieën:

Hier is Google:

Hier is de eenvoudige LinkedIn:

Maar Facebook is de eenvoudigste en minst kieskeurige (houd de foto's van de borstvoeding tot een minimum beperkt):

Mailgun e-mail

Omdat ik Mailgun gebruik om e-mails te bezorgen, heb ik met hen een domeinaccount voor Simple Planner gemaakt:

Dit vereiste zorgvuldige wijzigingen bij mijn domeinregistrar:

SSL 

Ik gebruik Let's Encrypt voor de https (SSL) met Meeting Planner en de bijbehorende back-end administratieve service. Het opzetten van die dingen verliep redelijk soepel voor mij. Maar het toevoegen van SimplePlanner.io werkte niet en ik moest uiteindelijk aparte Apache (.conf) configuratiebestanden maken voor SP en de bestanden zelf aanpassen.

Dit is het http sp.conf-bestand dat doorverwijst naar https:

 Servernaam simpleplanner.io ServerAlias ​​www.simpleplanner.io DocumentRoot "/ var / www / mp / frontend / web"  AllowOverride All  RewriteEngine op RewriteCond% SERVER_NAME = simpleplanner.io [OR] RewriteCond% SERVER_NAME = www.simpleplanner.io RewriteRule ^ https: //% SERVER_NAME% REQUEST_URI [END, QSA, R = permanent] 

De omleidingen werkten gewoon niet met twee servernamen en twee serveraliassen. Dit is het bestand sp-ssl.conf:

  Servernaam simpleplanner.io ServerAlias ​​www.simpleplanner.io DocumentRoot "/ var / www / mp / frontend / web"  AllowOverride All  RewriteEngine On RewriteCond% SERVER_NAME = www.simpleplanner.io RewriteRule ^ https: //simpleplanner.io% REQUEST_URI [END, QSA, R = permanent] SSLCertificateFile /etc/letsencrypt/live/simpleplanner.io/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/simpleplanner.io/privkey.pem Inclusief /etc/letsencrypt/options-ssl-apache.conf SSLCertificateChainFile /etc/letsencrypt/live/simpleplanner.io/chain.pem   

Zo heb ik uiteindelijk de SSL-certificaten gemaakt met Let's Encrypt:

cd / opt / letsencrypt / sudo ./letsencrypt-auto --apache -d simpleplanner.io -d www.simpleplanner.io -d 

Let's Encrypt is geweldig, maar ze zijn nog jong en hoewel hun scripts steeds robuuster worden, zijn er nog steeds af en toe problemen zoals deze.

E-mail differentiatie op de achtergrond

De grootste uitdaging voor mij bij het starten van Simple Planner was het verwerken van e-mails op de achtergrond, zodat deze afkomstig waren van de juiste service, namelijk MP of SP, en al hun links deden dit ook.

Omdat ik de database uniform hield voor SP en MP, liet ik ook de verwerking van de achtergrond achter op het MP-domein. Dus moest ik de database uitbreiden zodat de tabel Gebruiker en vergadering een a Website ID kolom om aan te geven welke service elke vermelding heeft aangemaakt.

Dit is de Yii-databasemigratie:

db-> driverName === 'mysql') $ tableOptions = 'KARAKTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB';  $ this-> addColumn ('% meeting', site_id ', Schema :: TYPE_SMALLINT.' NOT NULL DEFAULT 0 '); $ this-> addColumn ('% user', site_id ', Schema :: TYPE_SMALLINT.' NOT NULL DEFAULT 0 ');  public function down () $ this-> dropColumn ('% meeting', site_id '); $ This-> dropColumn ( '% user', 'site_id');  

Ik gebruikte de BeforeSave () methode in het Meeting-model om altijd een Website ID op basis van de huidige service. Wellicht herinnert u zich nog dat ik deze methode heb gemaakt om altijd een veilig uniek ID voor elke vergadering te genereren:

public function beforeSave ($ insert) if (parent :: beforeSave ($ insert)) if ($ insert) $ this-> identifier = Yii :: $ app-> security-> generateRandomString (8); $ this-> site_id = Yii :: $ app-> params ['site'] ['id'];  return true; 

Ik heb een soortgelijke gemaakt voor het gebruikersmodel:

public function beforeSave ($ insert) if (parent :: beforeSave ($ insert)) if ($ insert) $ this-> site_id = Yii :: $ app-> params ['site'] ['id'] ;  return true;  

Gedurende de dienst gebruik ik de MiscHelpers :: buildCommand () methode om veilige koppelingen te maken waarbij gebruikers niet telkens hoeven in te loggen als ze op een e-mail reageren.

Ik dacht dat het gemakkelijk zou zijn om deze opdracht op één plaats aan te passen om te linken naar het juiste domein van de site. Dit zou echter vereisen dat deze veel gebruikte methode de vergadertafel opvraagt ​​voor de Website ID herhaaldelijk. Bijvoorbeeld de buildCommand () wordt voor elke deelnemer een aantal keren voor elke vergadering gebeld.

Om prestatieredenen heb ik besloten om alle oproepen naar deze functie te wijzigen om de Website ID. Al deze oproepen moesten bijvoorbeeld worden gewijzigd zoals hieronder getoond:

 foreach ($ this-> deelnemers als $ p) if ($ p-> status! = deelnemer: STATUS_DEFAULT) doorgaan;  // Bouw de absolute links naar de vergadering en commandeert $ auth_key = \ common \ models \ User :: find () -> where (['id' => $ p-> participant_id]) -> one () -> inlogcode; $ links = ['home' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_HOME, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'view' => MiscHelpers: : buildCommand ($ this-> id, Meeting :: COMMAND_VIEW, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'finalize' => MiscHelpers :: buildCommand ($ this-> id, Meeting: : COMMAND_FINALIZE, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'cancel' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_CANCEL, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'decline' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_DECLINE, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), ' acceptall '=> MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_ACCEPT_ALL, 0, $ p-> participant_id, $ auth_key, $ this-> site_id),' acceptplaces '=> MiscHelpers :: buildCommand ($ this -> id, Meeting :: COMMAND_ACCEPT_ALL_PLACES, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'accepttimes' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_ACCEPT_ALL_TIMES, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'addplace' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_ADD_PLACE, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'addtime' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_ADD_TIME, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'addnote' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_ADD_NOTE, 0, $ p -> participant_id, $ auth_key, $ this-> site_id), 'footer_email' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_FOOTER_EMAIL, 0, $ p-> participant_id, $ auth_key, $ this-> site_id), 'footer_block' => MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_FOOTER_BLOCK, $ this-> owner_id, $ p-> participant_id, $ auth_key, $ this-> site_id), 'footer_block_all' = > MiscHelpers :: buildCommand ($ this-> id, Meeting :: COMMAND_FOOTER_BLOCK_ALL, 0, $ p-> participant_id, $ auth_key, $ this-> site_id),]; 

Het Meeting-model wordt altijd voorafgaand aan deze oproepen geladen, dus toegang tot de Website ID is het snelst vanaf hier.

Wat is het volgende?

Hoewel alle configuraties en kleine variabelewijzigingen van deze site na een tijdje een beetje vervelend waren om te bouwen, laat het een aantal coole functies van het Yii Framework zien. Als je dit nog niet hebt gedaan, probeer dan de planning op de nieuwe site Simple Planner. 

Deel Meeting Planner met uw zakenpartners en Simple Planner met uw vrienden en familie.

Heb je je eigen gedachten? Ideeën? Feedback? Je kunt me altijd rechtstreeks op Twitter @reifman bereiken. Kijk hier voor komende tutorials in de Building Your Startup With PHP-serie.

Ik kom ook dichter bij het lanceren van het experiment met WeFunder op basis van de implementatie van de nieuwe crowdfundingregels van de SEC. Je kunt ons profiel daar volgen als je dat wilt. Ik zal hier ook meer over schrijven in een toekomstige tutorial.

Gerelateerde Links

  • Simple Planner of Meeting Planner
  • De WeFunder-pagina van Meeting Planner
  • Programmeren met Yii2: Aan de slag