Bu işe başlamanız için mükemmel olmanız gerekmiyor. Bu işte öğrenmenin sonu yok zaten. Bi'şeyler ürettiğiniz sürece teknik anlamda her şeyi bilseniz bile kurgusal anlamda sürekli gelişiyorsunuz.
Freelance iş alırken işin sadece HTML, CSS ve JS ile tamamlanabilecek işleri seçebilirsiniz. Bu tip siteler bir yönetim paneli olmayan, iletişim formu olmayan, çok fazla ürün içermeyen siteler olacaktır. Hatta muhtemelen karşına sıklıkla zaten hazırlanmış bir tema üzerinde değişiklikler yapman gereken işler çıkacaktır.
Eğer kendinize çok sayfalı bir kişisel web sitesi yapabilmişseniz hazırsın demektir. Ama bu kişisel web sitesini yaparken HTML, CSS, JS ile çözülebileceği halde çözemeyip es geçtiğiniz kurgular varsa hazır değilsinizdir. Kendi sitenizde HTML,CSS,JS ile yapılabilecek aklınıza gelen her şeyi yapabilmişseniz hazırsınızdır.
Çoğunlukla HTML,CSS,JS ile freelance alacağınız işler basit işler olur. Müşteri "Kurumsal web sitesi teması satın aldım ama şimdi bunu kendime göre düzenlemem gerekiyor" der mesela. Burada sadece sitedeki görselleri ve metinleri değiştirmeniz, CSS ile sitenin genel temasını değiştirmeniz yeterli olur. Haliyle müşteri bu işin kolay olduğunu düşünüp az para teklif eder.
İşleri biraz daha büyütmek için PHP iyi bir başlangıç olacaktır. Çünkü bu tip işlerden yürürken sunucu taraflı bazı işlemler de yapmanız istenebilir. Örneğin iletişim formundan mail göndermek istiyorsanız ve 3.parti hizmetlere güvenmiyorsanız form'daki verileri alıp PHP'ye iletip PHP'nin mail atmasını sağlamanız gerekir mesela.
Bu gibi durumlarda müşteri sizi gelişmeye zorlar. "Site tamam da iletişim formu da ekleyemez miyiz?" der mesela. Zaman içinde bu itiklemelerle neyi öğrenmeye ihtiyacınız olduğunu da görmüş olursunuz.
Ayrıca müşterilerinizin sizi kandırmak için birçok oyunu olduğunu göreceksiniz. Örneğin size bütün siteyi yaptırdıktan sonra "şu ufak isteğimi de yapabilir misin?" der. Siz "elime yapışmaz yapayım da bitsin" dersiniz. Sonra müşteri "Çok güzel, harika oldu ama sadece şunu da eklesek ne güzel olur." der. Böyle böyle çaktırmadan zamanınızı yiyerek az paraya çok iş yaptırır.
Bir başka oyunda da müşteri bazı konuları muallakta bırakır. İş tamamlandıktan sonra "ben sana böyle dememiştim" der. Eğer müşteriyle yazılı olarak net bir görüşme yapmamışsanız sizi ekstra isteğini yapmazsanız işi almayacağını söyleyerek tehdit eder. Yine size ekstra bir işi uygun fiyatla yaptırma çabaları...
Başka bir oyunda da müşteri sizden birkaç örnek çalışma ister ve iyi fiyat teklif eder. Siz örnek çalışmayı gönderdiğinizde de başkasıyla anlaştığını söyleyip sizinle irtibatı keser ve başkasına sizin hazırlayıp ücretsiz olarak eline verdiğiniz siteyi uygun fiyata düzenletir.
Bazısı sizi baştan manipule eder. "Daha önce bi'yazılımcıyla çalıştım. Parasını da peşin peşin verdim. Sonra web sitemi tamamlamadan ortadan kayboldu." der. Güya güveni sarsılmıştır. İşin arka planındaysa genellikle bol bol revizeler isteyerek 1000 liraya 5000 liralık site yaptırmaya çalıştığı yazılımcı en sonunda dayanamayıp işi bırakmıştır ve aldığı para da o zamana kadar yaptığı işi zaten karşılamıyordur bile...
Klasik oyunlardan biri "sen bu işi uygun fiyata yap bunun arkasında daha bir sürü iş vereceğim sana" yalanıdır. Müşteri burada doğru söylüyor olsa bile o arkadan gelen işlerin hepsini de ucuza yapmanızı bekleyecektir. Sonraki işlerde fiyatı artırmaya kalktığınızda da bu maliyeti karşılayamayacağını söyler ve yerinize sizin ilk günkü halinize benzer birini arar. Bu tip müşteriler sizinle kapıları tamamen kapatmazlar. Çünkü güvenilir yazılımcı bulmak kolay değildir ve bulamazsa işi size yaptırmayı denemek isteyecektir.
Bunlar dışında ödemelerinizi geciktirenler, "bi'yerden para bekliyorum o gelsin sana da paranı vereceğim" deyip ortadan kaybolanlar, hatta en yüzsüzleri de işi yaparken çok iyi gidiyor deyip iş bitince agrasif tavırla yaptığınız işi beğenmediğini söyleyerek ödeme yapmayı tümden reddedenler...
Eğer böyle oyunlara karşı sabrınız varsa ve hatta kendiniz de bir oyun kurucuysanız freelance işler yapabilirsiniz ama genelde hem yazılımda hem insan manipulasyonunda iyi olmak, o sabır, pek rastlanan bir durum değil. Bunlarla uğraşamam ben derseniz tavsiyem bu işlere küsmek yerine bunlarla uğraşacak biriyle ortaklık kurmanız olur. Siz müşteriyle asla iletişime geçmezsiniz. İletişimi bir aracı arkadaşınız üzerinden yürütürsünüz. O arkadaşınız müşteriyle onun anlayacağı dilden konuşmayı da, yaptığınız eksik kalan işleri müşteriye kabul ettirmeyi de, parasını peşinen tahsil etmeyi de bilir. Siz daha az kazanırsınız belki ama kafanız rahat olur. Ayrıca kendinizi yazılım işinde geliştirmek için arkadaşınızdan zaman satın almış olursunuz.
Aslında demem o ki, başlayın ve kendi deneyimlerinizi yaşayın. Potansiyelinizi görün. Belki de bu işler tam sizliktir kim bilir?
En kötü ihtimalle müşterinizin işini teslim edemezsiniz. O zaman parasını da almazsınız olur biter. Ama edindiğiniz tecrube size kalır.
Eğer çok uygun bir fiyat öneriyorsanız müşteriyle bu işte yeni olduğunuzu, işi layıkıyla yapamama endişeniz olduğunu, işi tamamlayamazsanız parayı geri iade edeceğinizi veya hiç para almayacağınızı da dürüstçe söyleyebilirsiniz. Çoğu müşteri buna anlayışla yaklaşacaktır çünkü müşteri sizde minnet duygusu yaratarak gelecekte de size ucuza iş yaptırabileceğine inanır. Ayrıca müşterinin size gelecekte de iş yaptırma ihtimali varsa sizinle iletişimini kötü şekilde sonlandırmak istemez.
Ben de @munzevi gibi müşterinin böyle oyunlarla yaklaşanına veya anlamsızca kendini üstün görmesine tahammül edemediğimden iletişim problemleri yaşadığım için çok uzun zamandır freelance iş almıyorum.
Merhaba.
Ajax isteğinizi, her tuşa basıştan 300ms sonra atmanız gerekiyor. Tabii ki bu 300ms sayacı her tuşa basışta sıfırlanmalı.
let timeoutCounter = null; // 300ms'lik sayacımızı tutacak değişken.
$inputElement.on("input", function() {
clearTimeout(timeoutCounter); // input'a bir giriş olduğu için 300ms'lik sayacı sıfırlamak için temizleyelim. timeoutCounter değişkeni null'sa zaten bi'şey olmayacak. null değilse, setTimeout fonksiyonu iptal edilmiş olacak.
const text = $(this).val(); // İstekle gönderilecek text'i alalım.
if(!text) return; // text boşsa işlem yapılmayacak.
timeoutCounter = setTimeout(function() { // Sayacı sıfırlamıştık. Şimdi 300ms'ye tekrar kuralım ve sayacı timeoutCounter değişkenine atayalım.
$.ajax({ ... }); // Ajax isteği burada atılabilir. 300ms sonunda istek atılacak.
}, 300);
});
Herkese çok detaylı, uzun ve teknik cevaplar vermeye çalışmamın nedeni aslında cevabı doğrudan soruyu sorana yönelik yazmamam.
Aslında çoğu kez soruyu soranların zaten cevaplarımda anlattıklarımın çoğunu bildiklerini farkındayım.
Ama ben cevaplarımı, konu hakkında az bilgisi olup da araştırırken cevabımı gören olursa onun için de anlaşılır olsun diye düşünerek yazıyorum.
Ama tabi prototurk.com soru cevap bölümünün arama özelliği henüz iyileştirilmediği için çoğunlukla yazdıklarım boşa gidiyor tabi... :( Ama bir gün arama bölümü geliştirilirse kaynaklar hazır olsun istiyorum.
Tayfun Erbilen'e instagram üzerinden buraların geliştirilmesine destek olabileceğimi söyledim ama ya görmedi ya güvenmedi. :)
Açıkçası Chrome eklentisi yazmak üzerine "Merhaba Dünya" dışında pek pratiğim yok.
Extension yazarken storage'a bu şekilde veri basıldığını bile sorunuzdan öğrendim. :)
Cevabım çalışsaymış iyiymiş. :/
Ben sorunuzu görüp diğer cevaplarınızı da okuyup anlayıp cevap yazmaya başladığımda pes etmişsiniz :D
Doğru cevabınızı görmeden bir cevap yazmış oldum (aşağılarda bulabilirsiniz).
Benim cevabımı yazmaya başlamam asenkron bir fonksiyon olsun ve bir obje döndürecek olsun.
Sizin doğru cevap olarak işaretlediğiniz cevap da asenkron bir fonksiyon olsun ve o da obje döndürecek olsun.
Benim fonksiyonum sizinkinden önce başladı ama sonucu sizinkinden sonra döndü.
Eğer sizin cevabınızın sonunda console.log(ebykdrms.cevap, munzevi.cevap) diye bir komut olsaydı
normalde beklenen şey benim cevabım henüz oluşmadığı için (çünkü sizin fonksiyonunuzdayız) ilk parametre için undefined, ikinci parametre için sizin cevabınız olurdu.
Gel gelelim javascript (cevaplar obje oldukları için) ikimizin de cevabını ekrana basacaktı.
Ama şöyle doğrudan string'ler içeren bir komut verseydiniz:
console.log(ebykdrms.cevap.metin, munzevi.cevap.metin); o zaman ilk parametre undefined olarak görünecekti.
Bu da öylesine bir cevap olsun :D
Adım adım işlemleri okuduğumda şöyle bir işleyiş gördüm:
1) Sınıftan nesne oluşturduğunuz anda kurucu fonksiyon çalışıyor.
2) conf = {} ataması yapıyorsunuz.
3) storageData() fonksiyonunu çağırarak data = { lang: 'en', testData1: 1, testdata2: 2 } işlemini sağlyorsunuz.
4) datadaki veriyi Object.entries() işlevinden geçirip şu diziyi elde ediyorsunuz:
[ ["lang", "en"], ["testData1", 1], ["testData2", 2] ]
5) forEach() ile bu dizi üzerinde dönmeye başlıyorsunuz. Döngüyü adım adım işlersek:
5.1) key="lang" ve val="en" iken chrome.storage.local.get("lang", (obj) => {...}) işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünün 2.adımına geçiyorsunuz.
5.2) key="testData1" ve val=1 iken chrome.storage.local.get("testData1", (obj) => {...}) işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünün 3.adımına geçiyorsunuz.
5.3) key="testData2" ve val=2 iken chrome.storage.local.get("testData2", (obj) => {...}) işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünüz bitiyor.
6) Döngü bitti. Henüz asenkron fonksiyonlar sonuç döndürmedi. Yani getData2() fonksiyonu çalışmadığı için henüz conf.lang değeri de dolmadı.
7) conf.now key'ini doldurduğunuz sırada henüz conf.lang key'i tanımlanmadı.
console.log(this.conf, this.conf.lang) sizi yanıltıyor. İlk parametrede obje, ikinci parametrede string gönderdiğiniz için, javascript de objelere ve string'lere farklı yaklaşımlar sergilediği için size yanıltıcı sonuçlar gösteriyor.
console.log() fonksiyonu okunduğu andan ekrana basma işlemini gerçekleştirene kadar geçen sürede this.conf.lang değeri dolmuş oluyor.
Yani console.log(this.conf, this.conf.lang) komutunu verdiğiniz anda şunu söylemiş oluyorsunuz: console.log(obje, "string")
Ekrana basılma sırasında console.log() string değeri doğrudan hafızasında tutarken, ilk parametredeki objenin sadece referansını tutuyor. Bu yüzden ekrana basılırkenki string değer gerçekten console.log() komutu ilk çağırıldığındaki değer olurken obje değeri ekrana basılma anındaki değer neyse o oluyor.
Eğer objenin de o andaki değerini almak istiyorsanız komutu şu şekilde düzeltmelisiniz:
console.log(JSON.stringify(this.conf), this.conf.lang); Böylece iki parametrede de string değer aldığı için çağırıldığı andaki değerler neyse onu yazdıracaktır.
Yani sizin obje olarak basınca değer var ama direkt değeri basınca değer yok olarak bahsettiğiniz olay javascript'in objeleri tutuş biçimindeki yaklaşımından kaynaklanıyor. (Ki aslında bu tutuş biçimi javascript'in yüksek performanslı olmasını sağlayan en büyük etkenlerden biri)
Sorunun çözümü olarak, emin değilim ama, conf.now key'ine değer atama işlemini callback fonksiyonunun içinde yapmanız gerekiyor olabilir.
Yani:
constructor() {
this.conf = {};
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, obj => {
if(!obj[key]) chrome.storage.local.set({[key]:val}, this.getData2({[key]:val}));
else this.getData2(obj);
if(key=="lang") {
this.conf.now = new Intl.DateTimeFormat(this.conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: this.conf.timeZone
}).format(new Date(Date.now()));
console.log("in callback: ", JSON.parse(JSON.strinify(this.conf)), this.conf.lang);
}
});
});
console.log("in constructor: ", JSON.parse(JSON.strinify(this.conf)), this.conf.lang);
}
React ile site yazıyorsanız aslında tek bir sayfaya sahipsiniz.
React projenizden build aldığınızda size bir grup dosya çıkaracak. Bu dosyalar node.js ile artık bir bağı kalmamış dosyalardır.
.htaccess yardımıyla PHP'ye gelen istekleri buraya yönlendirirseniz react projeniz PHP sunucuda çalışacaktır.
Eğer veritabanından sorgu sonucu dönmüyorsa query() fonksiyonu false dönüyordur.
Bu durumda siz if koşulunda aslında false->rowCount() yazmış gibi oluyorsunuz.
Hata da bundan bahsediyor: boolean bir değer üzerinde sanki objeymiş gibi rowCount() fonksiyonu çağırıyorsun
Bu hatadan kurtulmak için if koşulunu değiştirmelisiniz.
$vList = $db->query("SELECT * FROM news where news_category='30'", PDO::FETCH_ASSOC);
if($vList){
foreach ($vList as $vkey => $List) {
$Dizi[] = $List["news_title"];
}
}
Çalışan bir örneği incelemek için codepen hesabıma bakabilirsiniz: https://codepen.io/ebykdrms/pen/yLKyLgX
codepen'den silinirse diye burada da kodları paylaşıyorum:
Diyelim ki şöyle bir html yapınız var:
<div id="content">
<div class="selectbox-wrapper">
<select name="product[]">
<option value=0 selected>ÜRÜN SEÇİNİZ</option>
</select>
<button>Sil</button>
</div>
<div class="button-wrapper">
<button disabled>+ Yeni Ekle</button>
</div>
</div>
Örnek olarak şöyle bir yapı kurulabilir:
// Seçilebilecek Ürünlerimiz
const products = [
{ id: 1, name: "Ürün 1" },
{ id: 2, name: "Ürün 2" },
{ id: 3, name: "Ürün 3" },
{ id: 4, name: "Ürün 4" }
];
// Seçilmiş ürünlerimizi tutacak bir dizi.
const selectedProducts = [];
/**************************************/
// DOM'dan kullanacağımız elementleri (başlangıçta hazır olanları) değişkenlere alıyoruz:
const $content = $("#content");
const $addNewProductButton = $content.find(".button-wrapper button").prop("disabled",true);
/**************************************/
// Bir selectbox'a istediğimiz ürünleri append edebildiğimiz fonksiyon:
function addProductsToSelectBox($selectbox, products) {
for(let i=0; i < products.length; i++) {
$selectbox.append('<option value="'+products[i].id+'">'+products[i].name+'</option>');
}
}
// Yeni select elementi ekleyen fonksiyon:
function addNewProductSelectBox($willCloneSelectBox) {
// Parametrelerden gelen elementi clone ediyoruz.
const $newProductSelectBox = $willCloneSelectBox.clone();
// Butonun üstüne bu klonu ekliyoruz.
$addNewProductButton.parent().before($newProductSelectBox.hide());
// Select elementinin bir fade efektiyle görünmesini sağlıyoruz.
$newProductSelectBox.fadeIn(300);
// Yeni bir select elementi geldiyse henüz seçim yapılmamıştır.
// Seçim yapılmamış bir select elementi varsa buton pasif olmalı.
$addNewProductButton.prop("disabled",true);
}
// Select elementinin yanındaki "Sil" butonuna tıklandığında...
$content.on("click",".selectbox-wrapper button", function(){
// Butonun kapsayan selectbox-wrapper elementini seçelim.
const $wrapper = $(this).closest(".selectbox-wrapper");
// Eğer bu tıklanan ilk selectbox ise (normalde olmamalı ama) silme fonksiyonu çalışmayacak.
if($wrapper.index()==0) return;
// Elementi direkt siliyoruz:
$wrapper.remove();
})
/**************************************/
// Başlangıçta elimizde olan ilk select elementine ürünleri ekliyoruz:
addProductsToSelectBox($content.find(".selectbox-wrapper > select"), products);
// "+ Yeni Ekle" butonuna tıklandığında...
$addNewProductButton.on("click",function(){
// Son eklenmiş olan .selectbox-wrapper klonlanarak yeni bir .selectbox-wrapper oluşturulacak:
addNewProductSelectBox($content.find(".selectbox-wrapper").last());
});
// Herhangi bir select elementinden seçim yapıldığında...
$content.on("change",".selectbox-wrapper select", function(){
// Değişim yaşanan select elementinin bize lazım verilerini alalım:
const selectboxIndex = $(this).parent().index();
const selectedValue = $(this).val();
const selectedText = $(this).find("option:selected").text();
// Eğer seçilen değer 0 ise (normalde olmamalı ama)
if(selectedValue=='0') {
// Boş bir select elementi olduğu için "+ Yeni Ekle" butonu pasif olacak.
$addNewProductButton.prop("disabled",true);
// selectedProducts dizindeki ilgili index'i boşaltıyoruz.
selectedProducts[selectboxIndex] = null;
// Fonksiyonun işi burada bitmiş olacağı için fonksiyonu sonlandırıyoruz.
return;
}
// selectedProducts dizisinin ilgili index'ine seçilen ürünü ekliyoruz.
selectedProducts[selectboxIndex] = { id: selectedValue, name: selectedText };
// selectedProducts dizisini kontrol edip, herhangi bir boş seçim var mı diye bakacağız.
for(let i=0; i<selectedProducts.length; i++) {
// Eğer seçim yapılmamış bir selectbox varsa buton pasif olmalı.
if(!selectedProducts[i]) {
$addNewProductButton.prop("disabled",true);
// Fonksiyonun işi burada bittiği için return ile fonksiyonu sonlandırıyoruz.
return;
}
// Fonksiyon bu satıra kadar gelebilmişse her şey yolundadır. Butonu aktif edebiliriz.
$addNewProductButton.prop("disabled",false);
}
});
Buradaki selectedProduct dizisine muhtemelen sizin ihtiyacınız yoktur.
Ben yazmayı özlemişim, yazdıkça yazdım... Kurgusal olarak kendinize bir örnek çıkarabilirsiniz umarım.
Facebook size bir API sağlıyor. Bu API'nin özelliklerini bilmiyorum ama muhtemelen erişim yetkiniz olan verilere bu API ile ulaşabilirsiniz.
https://apikutuphanesi.com/facebook-api-nedir
// $i=1 ve $x=0 olduğu durum:
for ($i = 1; 1 <= 9; $i++) {
for (0; 0 <= 400; $x += 200) {
switch (1) { /* $i değeri 3 veya 6 olmadığı için switch koşulu sağlanmadı*/ }
echo "i: 1<br>";
$img->crop(200, 200, 0, 0)->save('img/shared-photos/1.jpg');
}
}
// $i=1 ve $x=200 olduğu durum:
for ($i = 1; 1 <= 9; $i++) {
for (200; 200 <= 400; $x += 200) {
switch (1) { /* $i değeri 3 veya 6 olmadığı için switch koşulu sağlanmadı*/ }
echo "i: 1<br>";
$img->crop(200, 200, 200, 0)->save('img/shared-photos/1.jpg');
}
}
// $i=1 ve $x=400 olduğu durum:
for ($i = 1; 1 <= 9; $i++) {
for (400; 400 <= 400; $x += 200) {
switch (1) { /* $i değeri 3 veya 6 olmadığı için switch koşulu sağlanmadı*/ }
echo "i: 1<br>";
$img->crop(200, 200, 400, 0)->save('img/shared-photos/1.jpg');
}
}
// $i=1 ve $x=600 olduğu durum:
for ($i = 1; 1 <= 9; $i++) {
for (600; 600 <= 400; $x += 200) { /* $x değeri 400'den büyük olduğu için for koşulu sağlanmadı */ }
}
// $i=2 ve $x=600 olduğu durum:
for ($i = 1; 2 <= 9; $i++) {
for (600; 600 <= 400; $x += 200) { /* $x değeri 400'den büyük olduğu için for koşulu sağlanmadı */ }
}
// $i=3 ve $x=600 olduğu durum:
for ($i = 1; 3 <= 9; $i++) {
for (600; 600 <= 400; $x += 200) { /* $x değeri 400'den büyük olduğu için for koşulu sağlanmadı */ }
}
// $i=4 ve $x=600 olduğu durum:
for ($i = 1; 4 <= 9; $i++) {
for (600; 600 <= 400; $x += 200) { /* $x değeri 400'den büyük olduğu için for koşulu sağlanmadı */ }
}
Sorun şu ki;
$x değişkenine for içinde başlangıç değeri atamadığınız için her seferinde eski değerinden devam ediyor.
$x bir kez 400'ü geçtiğinde bir daha $x'i 0 yapacak bir komut satırına ulaşamıyor çünkü içteki döngü koşulu sağlanmadığı için switch sorgusuna ulaşılamıyor.
Aslında $i değeri 3 olduğunda döngü sonlanmıyor. $x değeri 600 olduğunda (yani içteki döngü 3 kez döndükten sonra) içteki döngünin koşulu sağlanmadığı için döngü geçersiz kalıyor.
Sizin bütün işlemleriniz de içteki döngüde olduğu için 3 kez çıktı elde edebilmiş oluyorsunuz.
Bu arada crop işlemi de aynı dosya adını birkaç kez kullanıyor. Yani içteki döngü ilk dönüşünde 1.jpg dosyası oluşuyor. İkinci dönüşünde yine 1.jpg dosyası oluşuyor. Üçüncü dönüşünde de yine 1.jpg dosyası oluşuyor. Tabi aynı dosya adı aynı klasörde olamayacağı için muhtemelen 2. ve 3. dönüşlerde 1.jpg dosyasının üzerine yazılyor.
Eğer içteki döngüde for($x=0; $x<=400; $x++) { şeklinde $x değerini sıfırlayamayacaksanız (kurgunuz buna uygun değilse) baştan kurgulamalısınız. Çünkü bu şekliyle döngüler sağlıklı çalışıyor olsaydı bile aynı dosyayı tekrar tekrar yazma sorununuz olacaktı.